aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format1
-rw-r--r--.gitignore11
-rw-r--r--MAINTAINERS23
-rw-r--r--Makefile75
-rw-r--r--build-root/Makefile2
-rw-r--r--build/external/Makefile12
-rw-r--r--build/external/mlx_rdma_dpdk_matrix.txt1
-rw-r--r--build/external/packages.mk6
-rw-r--r--build/external/packages/dpdk.mk5
-rw-r--r--build/external/packages/ipsec-mb.mk2
-rw-r--r--build/external/packages/octeon-roc.mk8
-rw-r--r--build/external/packages/rdma-core.mk3
-rw-r--r--build/external/packages/xdp-tools.mk6
-rw-r--r--docs/Makefile6
-rw-r--r--docs/aboutvpp/releasenotes/index.rst3
-rw-r--r--docs/aboutvpp/releasenotes/past.rst2
-rw-r--r--docs/aboutvpp/releasenotes/v24.06.rst494
-rw-r--r--docs/configuration/reference.rst30
l---------docs/developer/corefeatures/sr/sr_mpls.rst2
l---------docs/developer/plugins/dev_armada.rst1
l---------docs/developer/plugins/http.rst1
-rw-r--r--docs/developer/plugins/index.rst3
l---------docs/developer/plugins/marvell.rst1
-rw-r--r--docs/spelling_wordlist.txt6
-rw-r--r--docs/usecases/webapp.rst19
-rw-r--r--extras/configs/http/setup.http3
-rw-r--r--extras/hs-test/Makefile148
-rw-r--r--extras/hs-test/README.rst270
-rw-r--r--extras/hs-test/container.go373
-rw-r--r--extras/hs-test/cpu.go90
-rw-r--r--extras/hs-test/cpu_pinning_test.go30
-rw-r--r--extras/hs-test/docker/Dockerfile.build8
-rw-r--r--extras/hs-test/docker/Dockerfile.curl10
-rw-r--r--extras/hs-test/docker/Dockerfile.nginx2
-rw-r--r--extras/hs-test/docker/Dockerfile.nginx-http38
-rw-r--r--extras/hs-test/docker/Dockerfile.nginx-server6
-rw-r--r--extras/hs-test/docker/Dockerfile.vpp3
-rw-r--r--extras/hs-test/echo_test.go55
-rw-r--r--extras/hs-test/framework_test.go23
-rw-r--r--extras/hs-test/go.mod68
-rw-r--r--extras/hs-test/go.sum264
-rw-r--r--extras/hs-test/hs_test.sh147
-rw-r--r--extras/hs-test/hst_suite.go503
-rw-r--r--extras/hs-test/http_test.go1255
-rw-r--r--extras/hs-test/infra/address_allocator.go (renamed from extras/hs-test/address_allocator.go)4
-rw-r--r--extras/hs-test/infra/container.go537
-rw-r--r--extras/hs-test/infra/cpu.go209
-rw-r--r--extras/hs-test/infra/hst_suite.go642
-rw-r--r--extras/hs-test/infra/netconfig.go (renamed from extras/hs-test/netconfig.go)82
-rw-r--r--extras/hs-test/infra/suite_cpu_pinning.go112
-rw-r--r--extras/hs-test/infra/suite_envoy_proxy.go212
-rw-r--r--extras/hs-test/infra/suite_iperf_linux.go96
-rw-r--r--extras/hs-test/infra/suite_ldp.go203
-rw-r--r--extras/hs-test/infra/suite_nginx_proxy.go191
-rw-r--r--extras/hs-test/infra/suite_no_topo.go131
-rw-r--r--extras/hs-test/infra/suite_ns.go127
-rw-r--r--extras/hs-test/infra/suite_veth.go153
-rw-r--r--extras/hs-test/infra/suite_vpp_proxy.go211
-rw-r--r--extras/hs-test/infra/topo.go (renamed from extras/hs-test/topo.go)2
-rw-r--r--extras/hs-test/infra/utils.go316
-rw-r--r--extras/hs-test/infra/vppinstance.go646
-rw-r--r--extras/hs-test/iperf_linux_test.go48
-rw-r--r--extras/hs-test/ldp_test.go110
-rw-r--r--extras/hs-test/linux_iperf_test.go40
-rw-r--r--extras/hs-test/mem_leak_test.go24
-rw-r--r--extras/hs-test/mirroring_test.go24
-rw-r--r--extras/hs-test/nginx_test.go136
-rw-r--r--extras/hs-test/proxy_test.go141
-rw-r--r--extras/hs-test/raw_session_test.go32
-rw-r--r--extras/hs-test/resources/curl/write_out_download1
-rw-r--r--extras/hs-test/resources/curl/write_out_download_connect1
-rw-r--r--extras/hs-test/resources/curl/write_out_upload1
-rw-r--r--extras/hs-test/resources/curl/write_out_upload_connect1
-rw-r--r--extras/hs-test/resources/envoy/proxy.yaml16
-rw-r--r--extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf5
-rw-r--r--extras/hs-test/resources/nginx/nginx_server.conf43
-rw-r--r--extras/hs-test/resources/nginx/nginx_server_mirroring.conf13
-rwxr-xr-xextras/hs-test/script/build_boringssl.sh2
-rwxr-xr-xextras/hs-test/script/build_hst.sh48
-rwxr-xr-xextras/hs-test/script/build_nginx.sh2
-rwxr-xr-x[-rw-r--r--]extras/hs-test/script/compress.sh55
-rwxr-xr-xextras/hs-test/script/nginx_ldp.sh3
-rwxr-xr-xextras/hs-test/script/nginx_server_entrypoint.sh3
-rw-r--r--extras/hs-test/suite_nginx_test.go134
-rw-r--r--extras/hs-test/suite_no_topo_test.go110
-rw-r--r--extras/hs-test/suite_ns_test.go119
-rw-r--r--extras/hs-test/suite_tap_test.go84
-rw-r--r--extras/hs-test/suite_veth_test.go144
-rwxr-xr-xextras/hs-test/test91
-rw-r--r--extras/hs-test/tools/http_server/http_server.go4
-rw-r--r--extras/hs-test/topo-containers/2containers.yaml4
-rw-r--r--extras/hs-test/topo-containers/2peerVethLdp.yaml18
-rw-r--r--extras/hs-test/topo-containers/envoyProxy.yaml40
-rw-r--r--extras/hs-test/topo-containers/nginxProxy.yaml32
-rw-r--r--extras/hs-test/topo-containers/ns.yaml2
-rw-r--r--extras/hs-test/topo-containers/singleCpuPinning.yaml11
-rw-r--r--extras/hs-test/topo-containers/vppProxy.yaml (renamed from extras/hs-test/topo-containers/nginxProxyAndServer.yaml)21
-rw-r--r--extras/hs-test/utils.go80
-rw-r--r--extras/hs-test/vcl_test.go162
-rw-r--r--extras/hs-test/vppinstance.go412
-rwxr-xr-xextras/scripts/checkstyle.sh19
-rw-r--r--extras/strongswan/vpp_sswan/Makefile4
-rw-r--r--extras/vpp_stats_fs/stats_fs.go8
-rw-r--r--src/CMakeLists.txt15
-rw-r--r--src/cmake/api.cmake44
-rw-r--r--src/cmake/platform/cn913x.cmake3
-rw-r--r--src/cmake/platform/octeon9.cmake4
-rw-r--r--src/plugins/acl/acl.c211
-rw-r--r--src/plugins/avf/avf.h3
-rw-r--r--src/plugins/avf/device.c4
-rw-r--r--src/plugins/avf/format.c17
-rw-r--r--src/plugins/avf/input.c2
-rw-r--r--src/plugins/avf/output.c2
-rw-r--r--src/plugins/builtinurl/FEATURE.yaml13
-rw-r--r--src/plugins/builtinurl/builtins.c196
-rw-r--r--src/plugins/builtinurl/builtinurl.api43
-rw-r--r--src/plugins/builtinurl/builtinurl.c137
-rw-r--r--src/plugins/builtinurl/builtinurl.h57
-rw-r--r--src/plugins/builtinurl/builtinurl_test.c66
-rw-r--r--src/plugins/crypto_native/aes_cbc.c199
-rw-r--r--src/plugins/crypto_native/sha2.c18
-rw-r--r--src/plugins/crypto_openssl/main.c11
-rw-r--r--src/plugins/dev_armada/CMakeLists.txt30
-rw-r--r--src/plugins/dev_armada/README.rst61
-rw-r--r--src/plugins/dev_armada/musdk.h22
-rw-r--r--src/plugins/dev_armada/plugin.c12
-rw-r--r--src/plugins/dev_armada/pp2/format.c176
-rw-r--r--src/plugins/dev_armada/pp2/init.c343
-rw-r--r--src/plugins/dev_armada/pp2/port.c280
-rw-r--r--src/plugins/dev_armada/pp2/pp2.h144
-rw-r--r--src/plugins/dev_armada/pp2/queue.c48
-rw-r--r--src/plugins/dev_armada/pp2/rx.c158
-rw-r--r--src/plugins/dev_armada/pp2/tx.c83
-rw-r--r--src/plugins/dev_ena/ena.c2
-rw-r--r--src/plugins/dev_ena/port.c1
-rw-r--r--src/plugins/dev_iavf/adminq.c2
-rw-r--r--src/plugins/dev_iavf/counters.c1
-rw-r--r--src/plugins/dev_iavf/format.c1
-rw-r--r--src/plugins/dev_iavf/iavf.c2
-rw-r--r--src/plugins/dev_iavf/port.c15
-rw-r--r--src/plugins/dev_iavf/queue.c1
-rw-r--r--src/plugins/dev_iavf/virtchnl.c1
-rw-r--r--src/plugins/dev_iavf/virtchnl_funcs.h4
-rw-r--r--src/plugins/dev_octeon/CMakeLists.txt7
-rw-r--r--src/plugins/dev_octeon/counter.c337
-rw-r--r--src/plugins/dev_octeon/flow.c476
-rw-r--r--src/plugins/dev_octeon/format.c2
-rw-r--r--src/plugins/dev_octeon/init.c9
-rw-r--r--src/plugins/dev_octeon/octeon.h17
-rw-r--r--src/plugins/dev_octeon/port.c126
-rw-r--r--src/plugins/dev_octeon/queue.c1
-rw-r--r--src/plugins/dev_octeon/roc_helper.c7
-rw-r--r--src/plugins/dev_octeon/rx_node.c33
-rw-r--r--src/plugins/dev_octeon/tx_node.c68
-rw-r--r--src/plugins/dpdk/device/dpdk.h2
-rw-r--r--src/plugins/dpdk/device/dpdk_priv.h33
-rw-r--r--src/plugins/dpdk/device/init.c93
-rw-r--r--src/plugins/flowprobe/flowprobe.c4
-rw-r--r--src/plugins/hs_apps/CMakeLists.txt2
-rw-r--r--src/plugins/hs_apps/echo_client.c5
-rw-r--r--src/plugins/hs_apps/echo_server.c5
-rw-r--r--src/plugins/hs_apps/http_cli.c152
-rw-r--r--src/plugins/hs_apps/http_client_cli.c119
-rw-r--r--src/plugins/hs_apps/http_simple_post.c581
-rw-r--r--src/plugins/hs_apps/http_tps.c186
-rw-r--r--src/plugins/hs_apps/proxy.c31
-rw-r--r--src/plugins/hs_apps/test_builtins.c179
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test.h35
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test_client.c9
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test_protos.c429
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test_server.c6
-rw-r--r--src/plugins/http/CMakeLists.txt1
-rw-r--r--src/plugins/http/http.c1051
-rw-r--r--src/plugins/http/http.h869
-rw-r--r--src/plugins/http/http_buffer.c2
-rw-r--r--src/plugins/http/http_content_types.h19
-rw-r--r--src/plugins/http/http_header_names.h21
-rw-r--r--src/plugins/http/http_plugin.rst507
-rw-r--r--src/plugins/http/http_status_codes.h27
-rw-r--r--src/plugins/http/http_test.c34
-rw-r--r--src/plugins/http/http_timer.c2
-rw-r--r--src/plugins/http_static/builtinurl/json_urls.c65
-rw-r--r--src/plugins/http_static/http_cache.c28
-rw-r--r--src/plugins/http_static/http_cache.h7
-rw-r--r--src/plugins/http_static/http_static.api37
-rw-r--r--src/plugins/http_static/http_static.c35
-rw-r--r--src/plugins/http_static/http_static.h20
-rw-r--r--src/plugins/http_static/http_static_test.c91
-rw-r--r--src/plugins/http_static/static_server.c271
-rw-r--r--src/plugins/ikev2/CMakeLists.txt1
-rw-r--r--src/plugins/ikev2/ikev2.api6
-rw-r--r--src/plugins/ikev2/ikev2.c53
-rw-r--r--src/plugins/ikev2/ikev2_api.c1
-rw-r--r--src/plugins/ikev2/ikev2_crypto.c5
-rw-r--r--src/plugins/ikev2/ikev2_handoff.c165
-rw-r--r--src/plugins/ikev2/ikev2_priv.h6
-rw-r--r--src/plugins/linux-cp/lcp_interface.c11
-rw-r--r--src/plugins/mactime/builtins.c1
-rw-r--r--src/plugins/marvell/CMakeLists.txt47
-rw-r--r--src/plugins/marvell/README.rst85
-rw-r--r--src/plugins/marvell/pp2/cli.c135
-rw-r--r--src/plugins/marvell/pp2/format.c200
-rw-r--r--src/plugins/marvell/pp2/input.c392
-rw-r--r--src/plugins/marvell/pp2/output.c121
-rw-r--r--src/plugins/marvell/pp2/pp2.c403
-rw-r--r--src/plugins/marvell/pp2/pp2.h147
-rw-r--r--src/plugins/marvell/pp2/pp2_api.c100
-rw-r--r--src/plugins/marvell/pp2/pp2_test.c144
-rw-r--r--src/plugins/nat/nat44-ei/nat44_ei_in2out.c2
-rw-r--r--src/plugins/netmap/CMakeLists.txt32
-rw-r--r--src/plugins/netmap/FEATURE.yaml (renamed from extras/deprecated/netmap/FEATURE.yaml)6
-rw-r--r--src/plugins/netmap/cli.c (renamed from extras/deprecated/netmap/cli.c)4
-rw-r--r--src/plugins/netmap/device.c (renamed from extras/deprecated/netmap/device.c)4
-rw-r--r--src/plugins/netmap/net_netmap.h (renamed from extras/deprecated/netmap/net_netmap.h)6
-rw-r--r--src/plugins/netmap/netmap.api (renamed from extras/deprecated/netmap/netmap.api)0
-rw-r--r--src/plugins/netmap/netmap.c (renamed from extras/deprecated/netmap/netmap.c)93
-rw-r--r--src/plugins/netmap/netmap.h (renamed from extras/deprecated/netmap/netmap.h)0
-rw-r--r--src/plugins/netmap/netmap_api.c (renamed from extras/deprecated/netmap/netmap_api.c)54
-rw-r--r--src/plugins/netmap/node.c (renamed from extras/deprecated/netmap/node.c)20
-rw-r--r--src/plugins/netmap/plugin.c16
-rw-r--r--src/plugins/npt66/npt66.api13
-rw-r--r--src/plugins/npt66/npt66_node.c71
-rw-r--r--src/plugins/osi/CMakeLists.txt (renamed from src/plugins/builtinurl/CMakeLists.txt)20
-rw-r--r--src/plugins/osi/FEATURE.yaml11
-rw-r--r--src/plugins/osi/node.c (renamed from src/vnet/osi/node.c)2
-rw-r--r--src/plugins/osi/osi.c (renamed from src/vnet/osi/osi.c)14
-rw-r--r--src/plugins/osi/osi.h (renamed from src/vnet/osi/osi.h)0
-rw-r--r--src/plugins/osi/pg.c (renamed from src/vnet/osi/pg.c)2
-rw-r--r--src/plugins/osi/plugin.c (renamed from extras/deprecated/netmap/dir.dox)22
-rw-r--r--src/plugins/prom/prom.c12
-rw-r--r--src/plugins/pvti/CMakeLists.txt40
-rw-r--r--src/plugins/pvti/FEATURE.yaml8
-rw-r--r--src/plugins/pvti/api.c137
-rw-r--r--src/plugins/pvti/bypass-main.c79
-rw-r--r--src/plugins/pvti/bypass.c202
-rw-r--r--src/plugins/pvti/bypass.h53
-rw-r--r--src/plugins/pvti/input-main.c115
-rw-r--r--src/plugins/pvti/input.c496
-rw-r--r--src/plugins/pvti/input.h87
-rw-r--r--src/plugins/pvti/output-main.c85
-rw-r--r--src/plugins/pvti/output.c543
-rw-r--r--src/plugins/pvti/output.h75
-rw-r--r--src/plugins/pvti/pvti.api111
-rw-r--r--src/plugins/pvti/pvti.c479
-rw-r--r--src/plugins/pvti/pvti.h257
-rw-r--r--src/plugins/pvti/pvti_if.c376
-rw-r--r--src/plugins/pvti/pvti_if.h47
-rw-r--r--src/plugins/quic/quic.c10
-rw-r--r--src/plugins/srmpls/CMakeLists.txt30
-rw-r--r--src/plugins/srmpls/FEATURE.yaml (renamed from src/vnet/srmpls/FEATURE.yaml)0
-rw-r--r--src/plugins/srmpls/dir.dox (renamed from src/vnet/srmpls/dir.dox)0
-rw-r--r--src/plugins/srmpls/plugin.c (renamed from src/plugins/marvell/plugin.c)17
-rw-r--r--src/plugins/srmpls/sr_doc.rst (renamed from src/vnet/srmpls/sr_doc.rst)0
-rw-r--r--src/plugins/srmpls/sr_mpls.api (renamed from src/vnet/srmpls/sr_mpls.api)0
-rw-r--r--src/plugins/srmpls/sr_mpls.h (renamed from src/vnet/srmpls/sr_mpls.h)0
-rw-r--r--src/plugins/srmpls/sr_mpls_api.c (renamed from src/vnet/srmpls/sr_mpls_api.c)17
-rw-r--r--src/plugins/srmpls/sr_mpls_policy.c (renamed from src/vnet/srmpls/sr_mpls_policy.c)2
-rw-r--r--src/plugins/srmpls/sr_mpls_steering.c (renamed from src/vnet/srmpls/sr_mpls_steering.c)2
-rw-r--r--src/plugins/srmpls/sr_mpls_test.c (renamed from src/vnet/srmpls/sr_mpls_test.c)8
-rw-r--r--src/plugins/tlsopenssl/tls_openssl.c5
-rw-r--r--src/plugins/unittest/fib_test.c51
-rw-r--r--src/plugins/unittest/segment_manager_test.c5
-rw-r--r--src/plugins/unittest/session_test.c424
-rw-r--r--src/plugins/unittest/tcp_test.c5
-rw-r--r--src/plugins/unittest/util_test.c30
-rw-r--r--src/plugins/urpf/CMakeLists.txt4
-rw-r--r--src/plugins/urpf/urpf.c27
-rw-r--r--src/plugins/urpf/urpf.h14
-rw-r--r--src/plugins/urpf/urpf_dp.h335
-rw-r--r--src/plugins/wireguard/wireguard_chachapoly.c8
-rw-r--r--src/plugins/wireguard/wireguard_noise.c4
-rw-r--r--src/svm/svmdb.c2
-rwxr-xr-xsrc/tools/vppapigen/vppapigen.py22
-rwxr-xr-xsrc/tools/vppapigen/vppapigen_c.py58
-rw-r--r--src/vat/api_format.c2
-rw-r--r--src/vcl/vcl_locked.c8
-rw-r--r--src/vcl/vppcom.c124
-rw-r--r--src/vcl/vppcom.h4
-rw-r--r--src/vlib/buffer.c60
-rw-r--r--src/vlib/buffer.h3
-rw-r--r--src/vlib/cli.c106
-rw-r--r--src/vlib/format.c2
-rw-r--r--src/vlib/linux/pci.c15
-rw-r--r--src/vlib/main.c5
-rw-r--r--src/vlib/node.c49
-rw-r--r--src/vlib/node.h1
-rw-r--r--src/vlib/node_funcs.h4
-rw-r--r--src/vlib/threads.c21
-rw-r--r--src/vlib/threads.h10
-rw-r--r--src/vlib/unix/cli.c6
-rw-r--r--src/vlib/unix/main.c114
-rw-r--r--src/vlib/unix/plugin.c52
-rw-r--r--src/vlibapi/api_shared.c2
-rw-r--r--src/vnet/CMakeLists.txt53
-rw-r--r--src/vnet/dev/bus/pci.c (renamed from src/vnet/dev/pci.c)2
-rw-r--r--src/vnet/dev/bus/pci.h (renamed from src/vnet/dev/pci.h)0
-rw-r--r--src/vnet/dev/bus/platform.c172
-rw-r--r--src/vnet/dev/bus/platform.h27
-rw-r--r--src/vnet/dev/counters.c2
-rw-r--r--src/vnet/dev/dev.h3
-rw-r--r--src/vnet/dev/errors.h3
-rw-r--r--src/vnet/dev/format.c2
-rw-r--r--src/vnet/dev/port.c23
-rw-r--r--src/vnet/devices/tap/tap.c5
-rw-r--r--src/vnet/devices/tap/tap.h1
-rw-r--r--src/vnet/devices/virtio/FEATURE.yaml1
-rw-r--r--src/vnet/devices/virtio/cli.c4
-rw-r--r--src/vnet/devices/virtio/node.c10
-rw-r--r--src/vnet/devices/virtio/pci.c52
-rw-r--r--src/vnet/devices/virtio/pci.h82
-rw-r--r--src/vnet/devices/virtio/virtio.api1
-rw-r--r--src/vnet/devices/virtio/virtio.c1
-rw-r--r--src/vnet/devices/virtio/virtio.h1
-rw-r--r--src/vnet/devices/virtio/virtio_api.c8
-rw-r--r--src/vnet/devices/virtio/virtio_inline.h1
-rw-r--r--src/vnet/devices/virtio/virtio_std.h96
-rw-r--r--src/vnet/error.c4
-rw-r--r--src/vnet/fib/fib_api.c16
-rw-r--r--src/vnet/fib/fib_api.h1
-rw-r--r--src/vnet/fib/fib_entry.c12
-rw-r--r--src/vnet/fib/fib_entry_src.c12
-rw-r--r--src/vnet/fib/fib_table.h2
-rw-r--r--src/vnet/fib/fib_types.c4
-rw-r--r--src/vnet/fib/ip4_fib.c5
-rw-r--r--src/vnet/fib/ip6_fib.c495
-rw-r--r--src/vnet/fib/ip6_fib.h36
-rw-r--r--src/vnet/interface/runtime.c3
-rw-r--r--src/vnet/interface_api.c5
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.c32
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_types.c15
-rw-r--r--src/vnet/ip/icmp6.c2
-rw-r--r--src/vnet/ip/ip.api17
-rw-r--r--src/vnet/ip/ip.h2
-rw-r--r--src/vnet/ip/ip6.h5
-rw-r--r--src/vnet/ip/ip6_forward.c147
-rw-r--r--src/vnet/ip/ip6_input.h66
-rw-r--r--src/vnet/ip/ip6_ll_table.c78
-rw-r--r--src/vnet/ip/ip_api.c67
-rw-r--r--src/vnet/ip/ip_sas.c2
-rw-r--r--src/vnet/ip/ip_test.c54
-rw-r--r--src/vnet/ip/lookup.c11
-rw-r--r--src/vnet/ipsec/ah_decrypt.c8
-rw-r--r--src/vnet/ipsec/ah_encrypt.c8
-rw-r--r--src/vnet/ipsec/esp_decrypt.c16
-rw-r--r--src/vnet/ipsec/esp_encrypt.c53
-rw-r--r--src/vnet/ipsec/ipsec.c6
-rw-r--r--src/vnet/ipsec/ipsec.h2
-rw-r--r--src/vnet/ipsec/ipsec_input.c310
-rw-r--r--src/vnet/ipsec/ipsec_sa.h2
-rw-r--r--src/vnet/ipsec/ipsec_tun_in.c4
-rw-r--r--src/vnet/l2/l2_vtr.h13
-rw-r--r--src/vnet/llc/llc.c4
-rw-r--r--src/vnet/llc/node.c4
-rw-r--r--src/vnet/pg/output.c8
-rw-r--r--src/vnet/pg/pg.h9
-rw-r--r--src/vnet/session/application.c2
-rw-r--r--src/vnet/session/application_local.c11
-rw-r--r--src/vnet/session/application_namespace.c22
-rw-r--r--src/vnet/session/application_namespace.h4
-rw-r--r--src/vnet/session/application_worker.c7
-rw-r--r--src/vnet/session/mma_template.c3
-rw-r--r--src/vnet/session/session.api60
-rw-r--r--src/vnet/session/session.c92
-rw-r--r--src/vnet/session/session.h51
-rw-r--r--src/vnet/session/session_api.c260
-rw-r--r--src/vnet/session/session_cli.c326
-rw-r--r--src/vnet/session/session_lookup.c357
-rw-r--r--src/vnet/session/session_lookup.h16
-rw-r--r--src/vnet/session/session_node.c2
-rw-r--r--src/vnet/session/session_rules_table.c313
-rw-r--r--src/vnet/session/session_rules_table.h216
-rw-r--r--src/vnet/session/session_sdl.c768
-rw-r--r--src/vnet/session/session_sdl.h36
-rw-r--r--src/vnet/session/session_table.c15
-rw-r--r--src/vnet/session/session_table.h5
-rw-r--r--src/vnet/session/session_test.c23
-rw-r--r--src/vnet/snap/snap.c5
-rw-r--r--src/vnet/srv6/sr_localsid.c2
-rw-r--r--src/vnet/tcp/tcp.c9
-rw-r--r--src/vnet/tcp/tcp.h32
-rw-r--r--src/vnet/tcp/tcp_cli.c85
-rw-r--r--src/vnet/tcp/tcp_output.c6
-rw-r--r--src/vnet/tcp/tcp_timer.c1
-rw-r--r--src/vnet/tls/tls.c160
-rw-r--r--src/vnet/tls/tls.h8
-rw-r--r--src/vnet/tls/tls_inlines.h129
-rw-r--r--src/vnet/tls/tls_record.c279
-rw-r--r--src/vnet/tls/tls_record.h250
-rw-r--r--src/vnet/udp/udp.c39
-rw-r--r--src/vnet/udp/udp.h1
-rw-r--r--src/vnet/udp/udp_error.def1
-rw-r--r--src/vnet/udp/udp_inlines.h4
-rw-r--r--src/vnet/udp/udp_input.c1
-rw-r--r--src/vnet/unix/gdb_funcs.c64
-rw-r--r--src/vpp-api/python/pyproject.toml3
-rw-r--r--src/vpp-api/python/vpp_papi/vpp_serializer.py13
-rw-r--r--src/vpp-api/vapi/vapi.c13
-rw-r--r--src/vpp-api/vapi/vapi.h22
-rw-r--r--src/vpp/api/types.c15
-rw-r--r--src/vpp/api/types.h1
-rw-r--r--src/vpp/conf/startup.conf5
-rw-r--r--src/vpp/vnet/main.c4
-rw-r--r--src/vppinfra/CMakeLists.txt39
-rw-r--r--src/vppinfra/asm_mips.h351
-rw-r--r--src/vppinfra/asm_x86.c1947
-rw-r--r--src/vppinfra/asm_x86.h125
-rw-r--r--src/vppinfra/backtrace.c260
-rw-r--r--src/vppinfra/cache.h24
-rw-r--r--src/vppinfra/clib.h4
-rw-r--r--src/vppinfra/cpu.h1
-rw-r--r--src/vppinfra/crypto/aes_cbc.h203
-rw-r--r--src/vppinfra/devicetree.c347
-rw-r--r--src/vppinfra/devicetree.h72
-rw-r--r--src/vppinfra/format.c10
-rw-r--r--src/vppinfra/format.h2
-rw-r--r--src/vppinfra/format_ansi.h48
-rw-r--r--src/vppinfra/heap.c1
-rw-r--r--src/vppinfra/jsonformat.c13
-rw-r--r--src/vppinfra/linux/mem.c39
-rw-r--r--src/vppinfra/mem.h17
-rw-r--r--src/vppinfra/mem_dlmalloc.c57
-rw-r--r--src/vppinfra/mhash.c52
-rw-r--r--src/vppinfra/mhash.h5
-rw-r--r--src/vppinfra/stack.c75
-rw-r--r--src/vppinfra/stack.h26
-rw-r--r--src/vppinfra/test_mhash.c403
-rw-r--r--src/vppinfra/time.c7
-rw-r--r--src/vppinfra/time_range.c9
-rw-r--r--src/vppinfra/unix-misc.c79
-rw-r--r--test/Makefile82
-rw-r--r--test/asf/asfframework.py72
-rw-r--r--test/asf/test_adl.py2
-rw-r--r--test/asf/test_api_trace.py2
-rw-r--r--test/asf/test_http_static.py2
-rw-r--r--test/asf/test_lb_api.py6
-rw-r--r--test/asf/test_prom.py1
-rw-r--r--test/asf/test_quic.py12
-rw-r--r--test/asf/test_session.py12
-rw-r--r--test/asf/test_session_sdl.py297
-rw-r--r--test/asf/test_tcp.py10
-rw-r--r--test/asf/test_tls.py6
-rw-r--r--test/asf/test_vcl.py107
-rw-r--r--test/asf/test_vhost.py2
-rw-r--r--test/asf/vpp_session_sdl.py75
-rw-r--r--test/config.py5
-rw-r--r--test/framework.py3
-rw-r--r--test/patches/scapy-2.4.5/cdp.patch14
-rw-r--r--test/patches/scapy-2.4.5/ikev2.patch22
-rw-r--r--test/patches/scapy-2.4.5/ipsec.patch230
-rw-r--r--test/patches/scapy-2.4.5/ppp.patch45
-rw-r--r--test/patches/scapy-2.4.5/scapy-python312.patch590
-rw-r--r--test/requirements-3.txt463
-rw-r--r--test/requirements.txt4
-rw-r--r--test/template_ipsec.py36
-rw-r--r--test/test_arping.py4
-rw-r--r--test/test_bond.py7
-rw-r--r--test/test_cdp.py2
-rw-r--r--test/test_cnat.py4
-rw-r--r--test/test_det44.py2
-rw-r--r--test/test_dhcp.py18
-rw-r--r--test/test_dhcp6.py9
-rw-r--r--test/test_dns.py2
-rw-r--r--test/test_dslite.py4
-rw-r--r--test/test_dvr.py2
-rw-r--r--test/test_flowprobe.py26
-rw-r--r--test/test_geneve.py3
-rw-r--r--test/test_gre.py3
-rw-r--r--test/test_gso.py4
-rw-r--r--test/test_gtpu.py3
-rw-r--r--test/test_igmp.py2
-rw-r--r--test/test_ikev2.py71
-rw-r--r--test/test_interface_crud.py4
-rw-r--r--test/test_ip4.py66
-rw-r--r--test/test_ip4_vrf_multi_instance.py4
-rw-r--r--test/test_ip6.py13
-rw-r--r--test/test_ip6_nd_mirror_proxy.py6
-rw-r--r--test/test_ip6_vrf_multi_instance.py4
-rw-r--r--test/test_ip_mcast.py10
-rw-r--r--test/test_ip_session_redirect.py5
-rw-r--r--test/test_ipip.py4
-rw-r--r--test/test_ipsec_ah.py4
-rw-r--r--test/test_ipsec_esp.py5
-rw-r--r--test/test_ipsec_spd_flow_cache_input.py8
-rw-r--r--test/test_ipsec_spd_fp_input.py7
-rw-r--r--test/test_ipsec_tun_if_esp.py36
-rw-r--r--test/test_l2tp.py4
-rw-r--r--test/test_l3xc.py2
-rw-r--r--test/test_lacp.py3
-rw-r--r--test/test_lb.py3
-rw-r--r--test/test_linux_cp.py14
-rw-r--r--test/test_lisp.py3
-rw-r--r--test/test_lldp.py1
-rw-r--r--test/test_map.py2
-rw-r--r--test/test_map_br.py2
-rw-r--r--test/test_memif.py2
-rw-r--r--test/test_mpls.py4
-rw-r--r--test/test_mtu.py6
-rw-r--r--test/test_nat44_ed.py15
-rw-r--r--test/test_nat44_ed_output.py4
-rw-r--r--test/test_nat44_ei.py24
-rw-r--r--test/test_nat64.py6
-rw-r--r--test/test_nat66.py2
-rw-r--r--test/test_npt66.py2
-rw-r--r--test/test_pcap.py4
-rw-r--r--test/test_pg.py4
-rw-r--r--test/test_ping.py4
-rw-r--r--test/test_pipe.py2
-rw-r--r--test/test_pnat.py4
-rw-r--r--test/test_pppoe.py3
-rw-r--r--test/test_pvti.py1153
-rw-r--r--test/test_reassembly.py22
-rw-r--r--test/test_span.py7
-rw-r--r--test/test_srv6_ad.py2
-rw-r--r--test/test_srv6_ad_flow.py4
-rw-r--r--test/test_srv6_as.py2
-rw-r--r--test/test_srv6_mobile.py15
-rw-r--r--test/test_svs.py2
-rw-r--r--test/test_trace_filter.py4
-rw-r--r--test/test_udp.py26
-rw-r--r--test/test_urpf.py2
-rw-r--r--test/test_vlib.py7
-rw-r--r--test/test_vrrp.py2
-rw-r--r--test/test_vxlan.py4
-rw-r--r--test/test_vxlan6.py4
-rw-r--r--test/test_wireguard.py14
-rw-r--r--test/vm_vpp_interfaces.py10
-rw-r--r--test/vpp_ip_route.py10
-rw-r--r--test/vpp_pg_interface.py2
528 files changed, 28479 insertions, 11291 deletions
diff --git a/.clang-format b/.clang-format
index 617c5db417f..798a8c39a35 100644
--- a/.clang-format
+++ b/.clang-format
@@ -27,6 +27,7 @@ ForEachMacros:
- 'foreach_vlib_frame_bitmap_set_bit_index'
- 'FOREACH_ARRAY_ELT'
- 'RTE_ETH_FOREACH_DEV'
+ - 'foreach_clib_stack_frame'
- 'foreach_vnet_dev_rx_queue_runtime'
- 'foreach_vnet_dev_counter'
- 'foreach_vnet_dev_port_rx_queue'
diff --git a/.gitignore b/.gitignore
index 5537f0dd791..ea3ab8de5b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,13 @@
/build/external/vpp-*
/build/external/dpdk_mlx_default.sh
/build/external/downloads/
+/build/external/deb/_build/
+/build/external/deb/debian/changelog
+/build/external/deb/debian/debhelper-build-stamp
+/build/external/deb/debian/tmp/
+/build/external/deb/debian/files/
+/build/external/deb/debian/.debhelper/
+/build/external/deb/debian/vpp-ext-deps*
/path_setup
/tools/
# start autotools ignore
@@ -123,6 +130,8 @@ compile_commands.json
/extras/hs-test/hs-test
/extras/hs-test/http_server
/extras/hs-test/.build.ok
+/extras/hs-test/.build.cov.ok
+/extras/hs-test/.last_hst_ppid
/extras/hs-test/summary/
# ./configure
@@ -137,3 +146,5 @@ compile_commands.json
/cmake_install.cmake
/startup.conf
/startup.vpp
+# log file archives
+/archives
diff --git a/MAINTAINERS b/MAINTAINERS
index c310e728227..d6a9b1adf8f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -192,7 +192,7 @@ I: sr
M: Pablo Camarillo <pcamaril@cisco.com>
M: Ahmed Abdelsalam <ahabdels@cisco.com>
F: src/vnet/srv6/
-F: src/vnet/srmpls/
+F: src/plugin/srmpls/
F: src/examples/srv6-sample-localsid/
VNET IPSec
@@ -460,11 +460,6 @@ M: Dave Barach <vpp@barachs.net>
M: Florin Coras <fcoras@cisco.com>
F: src/plugins/http_static/
-Plugin - builtinurl
-I: builtinurl
-M: Dave Barach <vpp@barachs.net>
-F: src/plugins/builtinurl/
-
Plugin - GTPU
I: gtpu
M: Hongjun Ni <hongjun.ni@intel.com>
@@ -505,10 +500,10 @@ I: memif
M: Damjan Marion <damarion@cisco.com>
F: src/plugins/memif/
-Plugin - Marvell MUSDK device driver
-I: marvell
+Plugin - Marvell Armada device driver
+I: armada
M: Damjan Marion <damarion@cisco.com>
-F: src/plugins/marvell/
+F: src/plugins/dev_armada/
Plugin - performance counter
I: perfmon
@@ -852,6 +847,11 @@ I: tracenode
M: Maxime Peim <mpeim@cisco.com>
F: src/plugins/tracenode
+Plugin - Packet Vector Tunnel Interface
+I: pvti
+M: Andrew Yourtchenko <ayourtch@gmail.com>
+F: src/plugins/pvti
+
cJSON
I: cjson
M: Ole Troan <ot@cisco.com>
@@ -892,3 +892,8 @@ M: vpp-dev Mailing List <vpp-dev@fd.io>
C: Missing Maintainer
F: *
F: */
+
+Netmap
+I: netmap
+M: Tom Jones <thj@freebsd.org>
+F: src/plugins/netmap/
diff --git a/Makefile b/Makefile
index 6675b510153..98866e9be5f 100644
--- a/Makefile
+++ b/Makefile
@@ -72,7 +72,7 @@ DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-python
DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config
DEB_DEPENDS += gcovr lcov chrpath autoconf libnuma-dev
DEB_DEPENDS += python3-all python3-setuptools check
-DEB_DEPENDS += libffi-dev python3-ply
+DEB_DEPENDS += libffi-dev python3-ply libunwind-dev
DEB_DEPENDS += cmake ninja-build python3-jsonschema python3-yaml
DEB_DEPENDS += python3-venv # ensurepip
DEB_DEPENDS += python3-dev python3-pip
@@ -89,11 +89,21 @@ DEB_DEPENDS += tshark
DEB_DEPENDS += jq # for extracting test summary from .json report (hs-test)
LIBFFI=libffi6 # works on all but 20.04 and debian-testing
-
-ifeq ($(OS_VERSION_ID),22.04)
+ifeq ($(OS_VERSION_ID),24.04)
+ DEB_DEPENDS += libssl-dev
+ DEB_DEPENDS += llvm clang clang-format-15
+ # overwrite clang-format version to run `make checkstyle` successfully
+ # TODO: remove once ubuntu 20.04 is deprecated and extras/scripts/checkstyle.sh is upgraded to 15
+ export CLANG_FORMAT_VER=15
+ LIBFFI=libffi8
+ DEB_DEPENDS += enchant-2 # for docs
+else ifeq ($(OS_VERSION_ID),22.04)
DEB_DEPENDS += python3-virtualenv
DEB_DEPENDS += libssl-dev
- DEB_DEPENDS += clang clang-format-11
+ DEB_DEPENDS += clang clang-format-15
+ # overwrite clang-format version to run `make checkstyle` successfully
+ # TODO: remove once ubuntu 20.04 is deprecated and extras/scripts/checkstyle.sh is upgraded to 15
+ export CLANG_FORMAT_VER=15
LIBFFI=libffi7
DEB_DEPENDS += enchant-2 # for docs
else ifeq ($(OS_VERSION_ID),20.04)
@@ -102,9 +112,6 @@ else ifeq ($(OS_VERSION_ID),20.04)
DEB_DEPENDS += clang clang-format-11
LIBFFI=libffi7
DEB_DEPENDS += enchant-2 # for docs
-else ifeq ($(OS_VERSION_ID),20.10)
- DEB_DEPENDS += clang clang-format-11
- LIBFFI=libffi8ubuntu1
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-10)
DEB_DEPENDS += virtualenv
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-11)
@@ -113,9 +120,10 @@ else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-11)
LIBFFI=libffi7
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-12)
DEB_DEPENDS += virtualenv
- DEB_DEPENDS += clang-14 clang-format-14
+ DEB_DEPENDS += clang-14 clang-format-15
# for extras/scripts/checkstyle.sh
- export CLANG_FORMAT_VER=14
+ # TODO: remove once ubuntu 20.04 is deprecated and extras/scripts/checkstyle.sh is upgraded to -15
+ export CLANG_FORMAT_VER=15
LIBFFI=libffi8
else
DEB_DEPENDS += clang-11 clang-format-11
@@ -245,6 +253,7 @@ help:
@echo " build - build debug binaries"
@echo " build-release - build release binaries"
@echo " build-coverity - build coverity artifacts"
+ @echo " build-vpp-gcov - build gcov vpp only"
@echo " rebuild - wipe and build debug binaries"
@echo " rebuild-release - wipe and build release binaries"
@echo " run - run debug binary"
@@ -252,6 +261,8 @@ help:
@echo " debug - run debug binary with debugger"
@echo " debug-release - run release binary with debugger"
@echo " test - build and run tests"
+ @echo " test-cov-hs - build and run host stack tests with coverage"
+ @echo " test-cov-both - build and run python and host stack tests, merge coverage data"
@echo " test-help - show help on test framework"
@echo " run-vat - run vpp-api-test tool"
@echo " pkg-deb - build DEB packages"
@@ -268,8 +279,10 @@ help:
@echo " checkstyle-commit - check commit message format"
@echo " checkstyle-python - check python coding style using 'black' formatter"
@echo " checkstyle-api - check api for incompatible changes"
+ @echo " checkstyle-go - check style of .go source files"
@echo " fixstyle - fix coding style"
@echo " fixstyle-python - fix python coding style using 'black' formatter"
+ @echo " fixstyle-go - format .go source files"
@echo " doxygen - DEPRECATED - use 'make docs'"
@echo " bootstrap-doxygen - DEPRECATED"
@echo " wipe-doxygen - DEPRECATED"
@@ -278,6 +291,7 @@ help:
@echo " json-api-files - (re)-generate json api files"
@echo " json-api-files-debug - (re)-generate json api files for debug target"
@echo " go-api-files - (re)-generate golang api files"
+ @echo " cleanup-hst - stops and removes all docker contaiers and namespaces"
@echo " docs - Build the Sphinx documentation"
@echo " docs-venv - Build the virtual environment for the Sphinx docs"
@echo " docs-clean - Remove the generated files from the Sphinx docs"
@@ -440,6 +454,10 @@ rebuild: wipe build
build-release: $(BR)/.deps.ok
$(call make,$(PLATFORM),$(addsuffix -install,$(TARGETS)))
+.PHONY: build-vpp-gcov
+build-vpp-gcov:
+ $(call test,vpp_gcov)
+
.PHONY: wipe-release
wipe-release: test-wipe $(BR)/.deps.ok
$(call make,$(PLATFORM),$(addsuffix -wipe,$(TARGETS)))
@@ -457,6 +475,7 @@ define test
VPP_BIN=$(BR)/install-$(1)-native/vpp/bin/vpp \
VPP_INSTALL_PATH=$(BR)/install-$(1)-native/ \
EXTENDED_TESTS=$(EXTENDED_TESTS) \
+ DECODE_PCAPS=$(DECODE_PCAPS) \
TEST_GCOV=$(TEST_GCOV) \
PYTHON=$(PYTHON) \
OS_ID=$(OS_ID) \
@@ -486,6 +505,20 @@ test-cov:
$(eval TEST_GCOV=1)
$(call test,vpp_gcov,cov)
+.PHONY: test-cov-hs
+test-cov-hs:
+ @$(MAKE) -C extras/hs-test build-cov
+ @$(MAKE) -C extras/hs-test test-cov
+
+.PHONY: test-cov-both
+test-cov-both:
+ @echo "Running Python, Golang tests and merging coverage reports."
+ find $(BR) -name '*.gcda' -delete
+ @$(MAKE) test-cov
+ find $(BR) -name '*.gcda' -delete
+ @$(MAKE) test-cov-hs
+ @$(MAKE) cov-merge
+
.PHONY: test-cov-build
test-cov-build:
$(eval CC=gcc)
@@ -502,6 +535,14 @@ test-cov-post:
$(eval CC=gcc)
$(call test,vpp_gcov,cov-post)
+.PHONY: cov-merge
+cov-merge:
+ @lcov --add-tracefile $(BR)/test-coverage-merged/coverage-filtered.info \
+ -a $(BR)/test-coverage-merged/coverage-filtered1.info -o $(BR)/test-coverage-merged/coverage-merged.info
+ @genhtml $(BR)/test-coverage-merged/coverage-merged.info \
+ --output-directory $(BR)/test-coverage-merged/html
+ @echo "Code coverage report is in $(BR)/test-coverage-merged/html/index.html"
+
.PHONY: test-all
test-all:
$(eval EXTENDED_TESTS=1)
@@ -551,7 +592,7 @@ test-shell-cov:
.PHONY: test-dep
test-dep:
- @make -C test test-dep
+ @$(MAKE) -C test test-dep
.PHONY: test-doc
test-doc:
@@ -711,6 +752,10 @@ json-api-files-debug:
go-api-files: json-api-files
$(WS_ROOT)/src/tools/vppapigen/generate_go.py $(ARGS)
+.PHONY: cleanup-hst
+cleanup-hst:
+ $(MAKE) -C extras/hs-test cleanup-hst
+
.PHONY: ctags
ctags: ctags.files
@ctags --totals --tag-relative=yes -L $<
@@ -748,8 +793,16 @@ checkstyle-test:
checkstyle-python:
@$(MAKE) -C test checkstyle-python-all
+.PHONY: checkstyle-go
+checkstyle-go:
+ @$(MAKE) -C extras/hs-test checkstyle-go
+
+.PHONY: fixstyle-go
+fixstyle-go:
+ @$(MAKE) -C extras/hs-test fixstyle-go
+
.PHONY: checkstyle-all
-checkstyle-all: checkstyle-commit checkstyle checkstyle-python docs-spell
+checkstyle-all: checkstyle-commit checkstyle checkstyle-python docs-spell checkstyle-go
.PHONY: fixstyle
fixstyle:
diff --git a/build-root/Makefile b/build-root/Makefile
index d69a94c1e41..df4434d7629 100644
--- a/build-root/Makefile
+++ b/build-root/Makefile
@@ -997,7 +997,7 @@ $(PLATFORM_IMAGE_DIR)/ro.img ro-image: $(patsubst %,%-find-source,$(ROOT_PACKAGE
| xargs sign $($(PLATFORM)_public_key) \
$($(PLATFORM)_private_key_passphrase) ; \
fi ; \
- : make read-only file system ; \
+ : $(MAKE) read-only file system ; \
mksquashfs \
$${tmp_dir} $${ro_image} \
-no-exports -no-progress -no-recovery ; \
diff --git a/build/external/Makefile b/build/external/Makefile
index 046dd7484ae..ce57c79eec2 100644
--- a/build/external/Makefile
+++ b/build/external/Makefile
@@ -21,7 +21,7 @@ INSTALL_DIR ?= $(CURDIR)/_install
PKG_VERSION ?= $(shell git describe --abbrev=0 --match 'v[0-9]*' | cut -d- -f1 | cut -dv -f2 | cut -d. -f1,2)
PKG_SUFFIX ?= $(shell git log --oneline v$(PKG_VERSION)-rc0.. . | wc -l)
SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct .)
-ifeq ($shell(uname), FreeBSD)
+ifeq ($(shell uname), FreeBSD)
JOBS := $(shell nproc)
else
JOBS := $(if $(shell [ -f /proc/cpuinfo ] && head /proc/cpuinfo),\
@@ -30,6 +30,12 @@ endif # FreeBSD
B := $(BUILD_DIR)
I := $(INSTALL_DIR)
+ifeq ($(WORKSPACE),)
+L := $(B)
+else
+L := $(WORKSPACE)/archives/vpp-ext-deps
+$(shell rm -rf $(L) && mkdir -p $(L))
+endif
ifneq ($(shell which cmake3),)
CMAKE?=cmake3
@@ -94,7 +100,7 @@ build-deb: $(DEV_DEB)
install-deb:
ifneq ($(INSTALLED_VER),$(DEB_VER)-$(PKG_SUFFIX))
- @make $(DEV_DEB)
+ @$(MAKE) $(DEV_DEB)
@sudo dpkg -i $(DEV_DEB)
else
@echo "=========================================================="
@@ -175,7 +181,7 @@ ifeq ($(INSTALLED_VER)$(INSTALLED_RPM_VER),)
$(MAKE) config
else
ifneq ($(INSTALLED_VER),)
- make check-deb
+ $(MAKE) check-deb
endif
ifneq ($(INSTALLED_RPM_VER),)
$(MAKE) check-rpm
diff --git a/build/external/mlx_rdma_dpdk_matrix.txt b/build/external/mlx_rdma_dpdk_matrix.txt
index b2245cc49a0..17cf1aaa5e5 100644
--- a/build/external/mlx_rdma_dpdk_matrix.txt
+++ b/build/external/mlx_rdma_dpdk_matrix.txt
@@ -1,2 +1,3 @@
rdma=49.0 dpdk=23.11
rdma=51.0 dpdk=24.03
+rdma=52.0 dpdk=24.07
diff --git a/build/external/packages.mk b/build/external/packages.mk
index eba702222de..e8cbe271d9b 100644
--- a/build/external/packages.mk
+++ b/build/external/packages.mk
@@ -21,9 +21,9 @@ $1_src_dir ?= $(B)/src-$1
$1_patch_dir ?= $(CURDIR)/patches/$1_$($1_version)
$1_build_dir ?= $(B)/build-$1
$1_install_dir ?= $(I)
-$1_config_log ?= $(B)/$1.config.log
-$1_build_log ?= $(B)/$1.build.log
-$1_install_log ?= $(B)/$1.install.log
+$1_config_log ?= $(L)/$1.config.log
+$1_build_log ?= $(L)/$1.build.log
+$1_install_log ?= $(L)/$1.install.log
##############################################################################
# Download
diff --git a/build/external/packages/dpdk.mk b/build/external/packages/dpdk.mk
index c03d1a14703..d60a5a28ef3 100644
--- a/build/external/packages/dpdk.mk
+++ b/build/external/packages/dpdk.mk
@@ -19,9 +19,10 @@ DPDK_FAILSAFE_PMD ?= n
DPDK_MACHINE ?= default
DPDK_MLX_IBV_LINK ?= static
-dpdk_version ?= 24.03
+dpdk_version ?= 24.07
dpdk_base_url ?= http://fast.dpdk.org/rel
dpdk_tarball := dpdk-$(dpdk_version).tar.xz
+dpdk_tarball_md5sum_24.07 := 48151b1bd545cd95447979fa033199bb
dpdk_tarball_md5sum_24.03 := a98da848d6ba09808ef00f9a26aaa49a
dpdk_tarball_md5sum_23.11 := 896c09f5b45b452bd77287994650b916
dpdk_tarball_md5sum_23.07 := 2b6d57f077585cb15b885482362fd47f
@@ -138,7 +139,7 @@ DPDK_DRIVERS_DISABLED := $(shell echo $(DPDK_DRIVERS_DISABLED) | tr -d '\\\t ')
DPDK_LIBS_DISABLED := $(shell echo $(DPDK_LIBS_DISABLED) | tr -d '\\\t ')
SED=sed
-ifeq ($shell(uname), FreeBSD)
+ifeq ($(shell uname), FreeBSD)
SED=gsed
endif
diff --git a/build/external/packages/ipsec-mb.mk b/build/external/packages/ipsec-mb.mk
index d38f2e306ea..ffdbcd23fb2 100644
--- a/build/external/packages/ipsec-mb.mk
+++ b/build/external/packages/ipsec-mb.mk
@@ -32,7 +32,7 @@ define ipsec-mb_config_cmds
endef
define ipsec-mb_build_cmds
- @make -C $(ipsec-mb_src_dir)/lib -j \
+ @$(MAKE) -C $(ipsec-mb_src_dir)/lib -j \
SHARED=n \
SAFE_PARAM=n \
SAFE_LOOKUP=n \
diff --git a/build/external/packages/octeon-roc.mk b/build/external/packages/octeon-roc.mk
index 62f5d823bdf..98eb29b95aa 100644
--- a/build/external/packages/octeon-roc.mk
+++ b/build/external/packages/octeon-roc.mk
@@ -2,12 +2,12 @@
# SPDX-License-Identifier: Apache-2.0
# https://spdx.org/licenses/Apache-2.0.html
-octeon-roc_version := 0.3
-octeon-roc_tarball := octeon-roc-v$(octeon-roc_version).tar.gz
-octeon-roc_tarball_md5sum := e4a16beb76a6c63af1600dd4d1d752b8
+octeon-roc_version := 0.5
+octeon-roc_tarball := v$(octeon-roc_version).tar.gz
+octeon-roc_tarball_md5sum := 76bc56c84935da944bbf340fe5283ef0
octeon-roc_tarball_strip_dirs := 1
-octeon-roc_url := https://github.com/MarvellEmbeddedProcessors/marvell-vpp/archive/refs/tags/$(octeon-roc_tarball)
+octeon-roc_url := https://github.com/MarvellEmbeddedProcessors/marvell-octeon-roc/archive/refs/tags/$(octeon-roc_tarball)
define octeon-roc_config_cmds
@true
diff --git a/build/external/packages/rdma-core.mk b/build/external/packages/rdma-core.mk
index f015322775f..791a52c6ae3 100644
--- a/build/external/packages/rdma-core.mk
+++ b/build/external/packages/rdma-core.mk
@@ -23,10 +23,11 @@ RDMA_CORE_DEBUG?=n
# 2. Verify that the file build/external/dpdk_mlx_default.sh was generated
# and contains 'DPDK_MLX_DEFAULT=y'
#
-rdma-core_version := 51.0
+rdma-core_version := 52.0
rdma-core_tarball := rdma-core-$(rdma-core_version).tar.gz
rdma-core_tarball_md5sum_49.0 := 9fe3909f19c7e0276c9e546411bbb49c
rdma-core_tarball_md5sum_51.0 := ed95d79f782ea00bd7233d453abd60b3
+rdma-core_tarball_md5sum_52.0 := c78dba484aac72eb8586d88f7b399b0f
rdma-core_tarball_md5sum := $(rdma-core_tarball_md5sum_$(rdma-core_version))
rdma-core_tarball_strip_dirs := 1
rdma-core_url := http://github.com/linux-rdma/rdma-core/releases/download/v$(rdma-core_version)/$(rdma-core_tarball)
diff --git a/build/external/packages/xdp-tools.mk b/build/external/packages/xdp-tools.mk
index b65ae1361f5..b9285971f42 100644
--- a/build/external/packages/xdp-tools.mk
+++ b/build/external/packages/xdp-tools.mk
@@ -24,15 +24,15 @@ define xdp-tools_config_cmds
endef
define xdp-tools_build_cmds
- @cd ${xdp-tools_src_dir} && make V=1 BUILD_STATIC_ONLY=y > $(xdp-tools_build_log)
+ @cd ${xdp-tools_src_dir} && $(MAKE) V=1 BUILD_STATIC_ONLY=y > $(xdp-tools_build_log)
endef
define xdp-tools_install_cmds
@rm -f $(xdp-tools_install_log)
@cd ${xdp-tools_src_dir} && \
- make -C lib/libbpf/src install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log)
+ $(MAKE) -C lib/libbpf/src install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log)
@cd ${xdp-tools_src_dir} && \
- make libxdp_install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log)
+ $(MAKE) libxdp_install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log)
endef
$(eval $(call package,xdp-tools))
diff --git a/docs/Makefile b/docs/Makefile
index 01e8d659bff..9f209357113 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -70,7 +70,7 @@ venv:
spell: venv
@( \
. ${VENV_DIR}/bin/activate; \
- make -C ${SCRIPTS_DIR} generate && \
+ $(MAKE) -C ${SCRIPTS_DIR} generate && \
$(SPHINXBUILD) -b spelling $(SPHINXOPTS) $(BUILDDIR_SRC) $(BUILDDIR_OUT); \
)
@@ -81,7 +81,7 @@ rebuild-spell: clean spell
docs: venv
@( \
. ${VENV_DIR}/bin/activate; \
- make -C ${SCRIPTS_DIR} generate && \
+ $(MAKE) -C ${SCRIPTS_DIR} generate && \
$(SPHINXBUILD) $(SPHINXOPTS) -b html $(BUILDDIR_SRC) $(BUILDDIR_OUT); \
)
@@ -90,7 +90,7 @@ rebuild: clean docs
.PHONY: clean
clean:
- @make -C ${SCRIPTS_DIR} clean
+ @$(MAKE) -C ${SCRIPTS_DIR} clean
.PHONY: build
build: docs
diff --git a/docs/aboutvpp/releasenotes/index.rst b/docs/aboutvpp/releasenotes/index.rst
index adbe8401d27..df4232b1e83 100644
--- a/docs/aboutvpp/releasenotes/index.rst
+++ b/docs/aboutvpp/releasenotes/index.rst
@@ -6,9 +6,8 @@ Release notes
.. toctree::
:maxdepth: 2
+ v24.06
v24.02
v23.10
v23.06
- v23.02
- v22.10.1
past
diff --git a/docs/aboutvpp/releasenotes/past.rst b/docs/aboutvpp/releasenotes/past.rst
index 87285828eef..8f15bd4da3e 100644
--- a/docs/aboutvpp/releasenotes/past.rst
+++ b/docs/aboutvpp/releasenotes/past.rst
@@ -6,6 +6,8 @@ Past releases
.. toctree::
:maxdepth: 1
+ v23.02
+ v22.10.1
v22.10
v22.06.1
v22.06
diff --git a/docs/aboutvpp/releasenotes/v24.06.rst b/docs/aboutvpp/releasenotes/v24.06.rst
new file mode 100644
index 00000000000..73410b5dfaf
--- /dev/null
+++ b/docs/aboutvpp/releasenotes/v24.06.rst
@@ -0,0 +1,494 @@
+Release notes for VPP 24.06
+===========================
+
+More than 284 commits since the previous release, including 124 fixes.
+
+
+Features
+--------
+
+- Binary API Libraries
+
+ - Provide api definition over api (`ac0babd41 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ac0babd41>`_)
+
+- Build System
+
+ - Enable building on AlmaLinux 9 (`088d1a016 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=088d1a016>`_)
+
+- Plugins
+
+ - Crypto - OpenSSL
+
+ - Refactor openssl API usage (`97c9f5e7c <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=97c9f5e7c>`_)
+
+ - Crypto - native
+
+ - Add SHA2-HMAC (`9f2799fda <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=9f2799fda>`_)
+
+ - DPDK
+
+ - Bump to DPDK 24.03 (`a0fd52301 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=a0fd52301>`_)
+ - Bump rdma-core to 51.0 (`62af9bb64 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=62af9bb64>`_)
+
+ - Marvell Octeon device driver
+
+ - Add flow offload infra (`3d1459b14 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=3d1459b14>`_)
+ - Add support for VF device (`09c6cae8c <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=09c6cae8c>`_)
+ - Add support for SDP device (`043560ef2 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=043560ef2>`_)
+ - Add promisc support (`9abc01f25 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=9abc01f25>`_)
+ - Add support for mac address update (`b448568fa <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=b448568fa>`_)
+
+ - Wireguard
+
+ - Notify key changes to crypto engine (`6f8252e83 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=6f8252e83>`_)
+
+- VNET
+
+ - New Device Drivers Infra
+
+ - Add per-port vnet flow (`4af3fdfdd <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4af3fdfdd>`_)
+
+- Vector Library
+
+ - Improve automatic core pinning (`71c32a898 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=71c32a898>`_)
+
+
+Known issues
+------------
+
+For the full list of issues please refer to fd.io `JIRA <https://jira.fd.io>`_.
+
+Fixed issues
+------------
+
+For the full list of fixed issues please refer to:
+- fd.io `JIRA <https://jira.fd.io>`_
+- git `commit log <https://git.fd.io/vpp/log/?h=master>`_
+
+
+API changes
+-----------
+
+Description of results:
+
+- *Definition changed*: indicates that the API file was modified between releases.
+- *Only in image*: indicates the API is new for this release.
+- *Only in file*: indicates the API has been removed in this release.
+
+============================================================= ==================
+Message Name Result
+============================================================= ==================
+bpf_trace_filter_set_v2 only in image
+bpf_trace_filter_set_v2_reply only in image
+get_api_json only in image
+get_api_json_reply only in image
+ikev2_child_sa_v2_details only in image
+ikev2_child_sa_v2_dump only in image
+ikev2_sa_v2_details only in image
+ikev2_sa_v2_dump only in image
+ikev2_sa_v3_details only in image
+ikev2_sa_v3_dump only in image
+============================================================= ==================
+
+Found 10 api message signature differences
+
+
+Newly deprecated API messages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These messages are still there in the API, but can and probably
+will disappear in the next release.
+
+- builtinurl_enable
+- builtinurl_enable_reply
+
+In-progress API messages
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+These messages are provided for testing and experimentation only.
+They are *not* subject to any compatibility process,
+and therefore can arbitrarily change or disappear at *any* moment.
+Also they may have less than satisfactory testing, making
+them unsuitable for other use than the technology preview.
+If you are intending to use these messages in production projects,
+please collaborate with the feature maintainer on their productization.
+
+- abf_itf_attach_add_del
+- abf_itf_attach_add_del_reply
+- abf_itf_attach_details
+- abf_itf_attach_dump
+- abf_plugin_get_version
+- abf_plugin_get_version_reply
+- abf_policy_add_del
+- abf_policy_add_del_reply
+- abf_policy_details
+- abf_policy_dump
+- acl_plugin_use_hash_lookup_get
+- acl_plugin_use_hash_lookup_get_reply
+- acl_plugin_use_hash_lookup_set
+- acl_plugin_use_hash_lookup_set_reply
+- bpf_trace_filter_set
+- bpf_trace_filter_set_reply
+- bpf_trace_filter_set_v2
+- bpf_trace_filter_set_v2_reply
+- cnat_get_snat_addresses
+- cnat_get_snat_addresses_reply
+- cnat_session_details
+- cnat_session_dump
+- cnat_session_purge
+- cnat_session_purge_reply
+- cnat_set_snat_addresses
+- cnat_set_snat_addresses_reply
+- cnat_set_snat_policy
+- cnat_set_snat_policy_reply
+- cnat_snat_policy_add_del_exclude_pfx
+- cnat_snat_policy_add_del_exclude_pfx_reply
+- cnat_snat_policy_add_del_if
+- cnat_snat_policy_add_del_if_reply
+- cnat_translation_del
+- cnat_translation_del_reply
+- cnat_translation_details
+- cnat_translation_dump
+- cnat_translation_update
+- cnat_translation_update_reply
+- det44_get_timeouts_reply
+- det44_set_timeouts
+- det44_set_timeouts_reply
+- dev_attach
+- dev_attach_reply
+- dev_create_port_if
+- dev_create_port_if_reply
+- dev_detach
+- dev_detach_reply
+- dev_remove_port_if
+- dev_remove_port_if_reply
+- flowprobe_get_params
+- flowprobe_get_params_reply
+- flowprobe_interface_add_del
+- flowprobe_interface_add_del_reply
+- flowprobe_interface_details
+- flowprobe_interface_dump
+- flowprobe_set_params
+- flowprobe_set_params_reply
+- gbp_bridge_domain_add
+- gbp_bridge_domain_add_reply
+- gbp_bridge_domain_del
+- gbp_bridge_domain_del_reply
+- gbp_bridge_domain_details
+- gbp_bridge_domain_dump
+- gbp_bridge_domain_dump_reply
+- gbp_contract_add_del
+- gbp_contract_add_del_reply
+- gbp_contract_details
+- gbp_contract_dump
+- gbp_endpoint_add
+- gbp_endpoint_add_reply
+- gbp_endpoint_del
+- gbp_endpoint_del_reply
+- gbp_endpoint_details
+- gbp_endpoint_dump
+- gbp_endpoint_group_add
+- gbp_endpoint_group_add_reply
+- gbp_endpoint_group_del
+- gbp_endpoint_group_del_reply
+- gbp_endpoint_group_details
+- gbp_endpoint_group_dump
+- gbp_ext_itf_add_del
+- gbp_ext_itf_add_del_reply
+- gbp_ext_itf_details
+- gbp_ext_itf_dump
+- gbp_recirc_add_del
+- gbp_recirc_add_del_reply
+- gbp_recirc_details
+- gbp_recirc_dump
+- gbp_route_domain_add
+- gbp_route_domain_add_reply
+- gbp_route_domain_del
+- gbp_route_domain_del_reply
+- gbp_route_domain_details
+- gbp_route_domain_dump
+- gbp_route_domain_dump_reply
+- gbp_subnet_add_del
+- gbp_subnet_add_del_reply
+- gbp_subnet_details
+- gbp_subnet_dump
+- gbp_vxlan_tunnel_add
+- gbp_vxlan_tunnel_add_reply
+- gbp_vxlan_tunnel_del
+- gbp_vxlan_tunnel_del_reply
+- gbp_vxlan_tunnel_details
+- gbp_vxlan_tunnel_dump
+- gtpu_add_del_forward
+- gtpu_add_del_forward_reply
+- gtpu_add_del_tunnel_v2
+- gtpu_add_del_tunnel_v2_reply
+- gtpu_get_transfer_counts
+- gtpu_get_transfer_counts_reply
+- gtpu_tunnel_v2_details
+- gtpu_tunnel_v2_dump
+- ikev2_child_sa_v2_details
+- ikev2_child_sa_v2_dump
+- ikev2_initiate_del_child_sa
+- ikev2_initiate_del_child_sa_reply
+- ikev2_initiate_del_ike_sa
+- ikev2_initiate_del_ike_sa_reply
+- ikev2_initiate_rekey_child_sa
+- ikev2_initiate_rekey_child_sa_reply
+- ikev2_initiate_sa_init
+- ikev2_initiate_sa_init_reply
+- ikev2_nonce_get
+- ikev2_nonce_get_reply
+- ikev2_profile_add_del
+- ikev2_profile_add_del_reply
+- ikev2_profile_details
+- ikev2_profile_disable_natt
+- ikev2_profile_disable_natt_reply
+- ikev2_profile_dump
+- ikev2_profile_set_auth
+- ikev2_profile_set_auth_reply
+- ikev2_profile_set_id
+- ikev2_profile_set_id_reply
+- ikev2_profile_set_ipsec_udp_port
+- ikev2_profile_set_ipsec_udp_port_reply
+- ikev2_profile_set_liveness
+- ikev2_profile_set_liveness_reply
+- ikev2_profile_set_ts
+- ikev2_profile_set_ts_reply
+- ikev2_profile_set_udp_encap
+- ikev2_profile_set_udp_encap_reply
+- ikev2_sa_v3_details
+- ikev2_sa_v3_dump
+- ikev2_set_esp_transforms
+- ikev2_set_esp_transforms_reply
+- ikev2_set_ike_transforms
+- ikev2_set_ike_transforms_reply
+- ikev2_set_local_key
+- ikev2_set_local_key_reply
+- ikev2_set_responder
+- ikev2_set_responder_hostname
+- ikev2_set_responder_hostname_reply
+- ikev2_set_responder_reply
+- ikev2_set_sa_lifetime
+- ikev2_set_sa_lifetime_reply
+- ikev2_set_tunnel_interface
+- ikev2_set_tunnel_interface_reply
+- ikev2_traffic_selector_details
+- ikev2_traffic_selector_dump
+- ip_neighbor_config_get
+- ip_neighbor_config_get_reply
+- ip_route_add_del_v2
+- ip_route_add_del_v2_reply
+- ip_route_lookup_v2
+- ip_route_lookup_v2_reply
+- ip_route_v2_details
+- ip_route_v2_dump
+- ip_session_redirect_add
+- ip_session_redirect_add_reply
+- ip_session_redirect_add_v2
+- ip_session_redirect_add_v2_reply
+- ip_session_redirect_del
+- ip_session_redirect_del_reply
+- l2_emulation
+- l2_emulation_reply
+- lcp_default_ns_get_reply
+- lcp_default_ns_set
+- lcp_default_ns_set_reply
+- lcp_itf_pair_add_del_v2
+- lcp_itf_pair_add_del_v2_reply
+- lcp_itf_pair_add_del_v3
+- lcp_itf_pair_add_del_v3_reply
+- lcp_itf_pair_details
+- lldp_details
+- mdata_enable_disable
+- mdata_enable_disable_reply
+- nat44_ed_vrf_tables_v2_details
+- nat44_ed_vrf_tables_v2_dump
+- nat44_ei_add_del_address_range
+- nat44_ei_add_del_address_range_reply
+- nat44_ei_add_del_static_mapping
+- nat44_ei_add_del_static_mapping_reply
+- nat44_ei_address_details
+- nat44_ei_address_dump
+- nat44_ei_del_session
+- nat44_ei_del_session_reply
+- nat44_ei_del_user
+- nat44_ei_del_user_reply
+- nat44_ei_forwarding_enable_disable
+- nat44_ei_forwarding_enable_disable_reply
+- nat44_ei_ha_flush
+- nat44_ei_ha_flush_reply
+- nat44_ei_ha_resync
+- nat44_ei_ha_resync_completed_event
+- nat44_ei_ha_resync_reply
+- nat44_ei_ha_set_failover
+- nat44_ei_ha_set_failover_reply
+- nat44_ei_ha_set_listener
+- nat44_ei_ha_set_listener_reply
+- nat44_ei_interface_add_del_feature
+- nat44_ei_interface_add_del_feature_reply
+- nat44_ei_interface_details
+- nat44_ei_interface_dump
+- nat44_ei_ipfix_enable_disable
+- nat44_ei_ipfix_enable_disable_reply
+- nat44_ei_plugin_enable_disable
+- nat44_ei_plugin_enable_disable_reply
+- nat44_ei_set_addr_and_port_alloc_alg
+- nat44_ei_set_addr_and_port_alloc_alg_reply
+- nat44_ei_set_fq_options
+- nat44_ei_set_fq_options_reply
+- nat44_ei_set_mss_clamping
+- nat44_ei_set_mss_clamping_reply
+- nat44_ei_set_timeouts
+- nat44_ei_set_timeouts_reply
+- nat44_ei_set_workers
+- nat44_ei_set_workers_reply
+- nat44_ei_show_fq_options
+- nat44_ei_show_fq_options_reply
+- nat44_ei_show_running_config
+- nat44_ei_show_running_config_reply
+- nat44_ei_static_mapping_details
+- nat44_ei_static_mapping_dump
+- nat44_ei_user_details
+- nat44_ei_user_dump
+- nat44_ei_user_session_details
+- nat44_ei_user_session_dump
+- nat44_ei_user_session_v2_details
+- nat44_ei_user_session_v2_dump
+- nat44_ei_worker_details
+- nat44_ei_worker_dump
+- nat64_plugin_enable_disable
+- nat64_plugin_enable_disable_reply
+- npt66_binding_add_del
+- npt66_binding_add_del_reply
+- oddbuf_enable_disable
+- oddbuf_enable_disable_reply
+- pg_interface_enable_disable_coalesce
+- pg_interface_enable_disable_coalesce_reply
+- ping_finished_event
+- pnat_binding_add
+- pnat_binding_add_reply
+- pnat_binding_add_v2
+- pnat_binding_add_v2_reply
+- pnat_binding_attach
+- pnat_binding_attach_reply
+- pnat_binding_del
+- pnat_binding_del_reply
+- pnat_binding_detach
+- pnat_binding_detach_reply
+- pnat_bindings_details
+- pnat_bindings_get
+- pnat_bindings_get_reply
+- pnat_interfaces_details
+- pnat_interfaces_get
+- pnat_interfaces_get_reply
+- sample_macswap_enable_disable
+- sample_macswap_enable_disable_reply
+- set_ip_flow_hash_v3
+- set_ip_flow_hash_v3_reply
+- sr_localsids_with_packet_stats_details
+- sr_localsids_with_packet_stats_dump
+- sr_mobile_localsid_add_del
+- sr_mobile_localsid_add_del_reply
+- sr_mobile_policy_add
+- sr_mobile_policy_add_reply
+- sr_policies_with_sl_index_details
+- sr_policies_with_sl_index_dump
+- sr_policy_add_v2
+- sr_policy_add_v2_reply
+- sr_policy_mod_v2
+- sr_policy_mod_v2_reply
+- sw_interface_ip6nd_ra_details
+- sw_interface_ip6nd_ra_dump
+- sw_interface_set_vxlan_gbp_bypass
+- sw_interface_set_vxlan_gbp_bypass_reply
+- test_addresses
+- test_addresses2
+- test_addresses2_reply
+- test_addresses3
+- test_addresses3_reply
+- test_addresses_reply
+- test_empty
+- test_empty_reply
+- test_enum
+- test_enum_reply
+- test_interface
+- test_interface_reply
+- test_prefix
+- test_prefix_reply
+- test_string
+- test_string2
+- test_string2_reply
+- test_string_reply
+- test_vla
+- test_vla2
+- test_vla2_reply
+- test_vla3
+- test_vla3_reply
+- test_vla4
+- test_vla4_reply
+- test_vla5
+- test_vla5_reply
+- test_vla_reply
+- trace_capture_packets
+- trace_capture_packets_reply
+- trace_clear_cache
+- trace_clear_cache_reply
+- trace_clear_capture
+- trace_clear_capture_reply
+- trace_details
+- trace_dump
+- trace_dump_reply
+- trace_filter_function_details
+- trace_filter_function_dump
+- trace_set_filter_function
+- trace_set_filter_function_reply
+- trace_set_filters
+- trace_set_filters_reply
+- trace_v2_details
+- trace_v2_dump
+- tracenode_enable_disable
+- tracenode_enable_disable_reply
+- vxlan_gbp_tunnel_add_del
+- vxlan_gbp_tunnel_add_del_reply
+- vxlan_gbp_tunnel_details
+- vxlan_gbp_tunnel_dump
+- want_ping_finished_events
+- want_ping_finished_events_reply
+
+Patches that changed API definitions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+``src/vlibmemory/memclnt.api``
+
+* `ac0babd41 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ac0babd41>`_ api: provide api definition over api
+
+``src/plugins/builtinurl/builtinurl.api``
+
+* `a5668eb05 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=a5668eb05>`_ builtinurl: mark api as deprecated
+
+``src/plugins/netmap/netmap.api``
+
+* `16cc51b88 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=16cc51b88>`_ netmap: Reinstate and update netmap plugin
+
+``src/plugins/bpf_trace_filter/bpf_trace_filter.api``
+
+* `5be4b869a <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=5be4b869a>`_ bpf_trace_filter: support bpf filter optimization and dump
+
+``src/plugins/ikev2/ikev2.api``
+
+* `07b227407 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=07b227407>`_ ikev2: uptime
+* `f40a354da <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=f40a354da>`_ ikev2: dump state and profile name in CLI and API
+
+``src/plugins/ikev2/ikev2_types.api``
+
+* `07b227407 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=07b227407>`_ ikev2: uptime
+* `f40a354da <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=f40a354da>`_ ikev2: dump state and profile name in CLI and API
+
+``src/plugins/linux-cp/lcp.api``
+
+* `83ad79d69 <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=83ad79d69>`_ linux-cp: add add_del_v3 and get_v2 methods
+
+``src/plugins/srmpls/sr_mpls.api``
+
+* `182d8b2dd <https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=182d8b2dd>`_ sr: move srmpls to a plugin
diff --git a/docs/configuration/reference.rst b/docs/configuration/reference.rst
index 598e195af13..2a7726cccb4 100644
--- a/docs/configuration/reference.rst
+++ b/docs/configuration/reference.rst
@@ -497,6 +497,9 @@ The buffers Section
buffers-per-numa 128000
default data-size 2048
page-size default-hugepage
+ numa 1 {
+ buffers 64000
+ }
}
buffers-per-numa number
@@ -532,6 +535,33 @@ Set the page size for buffer allocation
page-size default
page-size default-hugepage
+numa <numa index> { .. }
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Settings specific to a single NUMA domain.
+
+.. code-block:: console
+
+ buffers {
+ numa 0 {
+ buffers 32768
+ }
+ }
+
+buffers <n>
+^^^^^^^^^^^^^^^
+
+The number of buffers allocated for this specific NUMA domain.
+If this is set to zero, no buffers are allocated for this domain.
+
+By default, the value configured in **buffers-per-numa** is used.
+
+.. code-block:: console
+
+ numa 0 {
+ buffers 32768
+ }
+
The dpdk Section
----------------
diff --git a/docs/developer/corefeatures/sr/sr_mpls.rst b/docs/developer/corefeatures/sr/sr_mpls.rst
index d2fe4025326..9e676db4f07 120000
--- a/docs/developer/corefeatures/sr/sr_mpls.rst
+++ b/docs/developer/corefeatures/sr/sr_mpls.rst
@@ -1 +1 @@
-../../../../src/vnet/srmpls/sr_doc.rst \ No newline at end of file
+../../../../src/plugins/srmpls/sr_doc.rst \ No newline at end of file
diff --git a/docs/developer/plugins/dev_armada.rst b/docs/developer/plugins/dev_armada.rst
new file mode 120000
index 00000000000..a545313ec9c
--- /dev/null
+++ b/docs/developer/plugins/dev_armada.rst
@@ -0,0 +1 @@
+../../../src/plugins/dev_armada/README.rst \ No newline at end of file
diff --git a/docs/developer/plugins/http.rst b/docs/developer/plugins/http.rst
new file mode 120000
index 00000000000..b0e49bbb721
--- /dev/null
+++ b/docs/developer/plugins/http.rst
@@ -0,0 +1 @@
+../../../src/plugins/http/http_plugin.rst \ No newline at end of file
diff --git a/docs/developer/plugins/index.rst b/docs/developer/plugins/index.rst
index 91af95f3318..393eefec535 100644
--- a/docs/developer/plugins/index.rst
+++ b/docs/developer/plugins/index.rst
@@ -19,9 +19,9 @@ For more on plugins please refer to :ref:`add_plugin`.
quic
cnat
+ dev_armada
lcp
srv6/index
- marvell
lldp
nat64
nat44_ei_ha
@@ -42,3 +42,4 @@ For more on plugins please refer to :ref:`add_plugin`.
bufmon_doc
ip_session_redirect_doc
bpf_trace_filter
+ http
diff --git a/docs/developer/plugins/marvell.rst b/docs/developer/plugins/marvell.rst
deleted file mode 120000
index 28f0cd0f664..00000000000
--- a/docs/developer/plugins/marvell.rst
+++ /dev/null
@@ -1 +0,0 @@
-../../../src/plugins/marvell/README.rst \ No newline at end of file
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 2348c8dc391..d6c5b97793e 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -805,6 +805,7 @@ operationalize
Optimisations
optimised
os
+osi
outacl
packagecloud
papi
@@ -902,6 +903,9 @@ pthreads
pton
pushingapatch
putatively
+pvti
+PVTI
+Pvti
pwait
py
pypi
@@ -1003,6 +1007,7 @@ screenshot
scrollbar
scrollbars
sctp
+sdl
sealert
seatbelt
seg
@@ -1047,6 +1052,7 @@ srcIP
srcPortDefinition
srcUP
srh
+srmpls
Srmpls
srtp
SRTP
diff --git a/docs/usecases/webapp.rst b/docs/usecases/webapp.rst
index f76fd5b6353..43e600467c5 100644
--- a/docs/usecases/webapp.rst
+++ b/docs/usecases/webapp.rst
@@ -19,8 +19,7 @@ why returning data in .json format tends to work out pretty well.
::
static int
- handle_get_status (http_builtin_method_type_t reqtype,
- u8 * request, http_session_t * hs)
+ handle_get_status (hss_url_handler_args_t *args)
{
my_main_t *mm = &my_main;
u8 *s = 0;
@@ -34,11 +33,11 @@ why returning data in .json format tends to work out pretty well.
s = format (s, "}}");
/* And tell the static server plugin how to send the results */
- hs->data = s;
- hs->data_offset = 0;
- hs->cache_pool_index = ~0;
- hs->free_data = 1; /* free s when done with it, in the framework */
- return 0;
+ args->data = s;
+ args->data_len = vec_len (s);
+ args->ct = HTTP_CONTENT_APP_JSON;
+ args->free_vec_data = 1; /* free s when done with it, in the framework */
+ return HSS_URL_HANDLER_OK;
}
Words to the Wise: Chrome has a very nice set of debugging tools. Select
@@ -53,7 +52,7 @@ considerable amount of time debugging .json bugs.
Step 2: Register URL handlers with the server
---------------------------------------------
-Call http_static_server_register_builtin_handler() as shown. It’s likely
+Call ``hss_register_url_handler`` as shown. It’s likely
but not guaranteed that the static server plugin will be available.
::
@@ -65,7 +64,7 @@ but not guaranteed that the static server plugin will be available.
/* Look up the builtin URL registration handler */
fp = vlib_get_plugin_symbol ("http_static_plugin.so",
- "http_static_server_register_builtin_handler");
+ "hss_register_url_handler");
if (fp == 0)
{
@@ -259,7 +258,7 @@ above:
::
- http static server www-root /myhugosite/public uri tcp://0.0.0.0/2345 cache-size 5m fifo-size 8192
+ http static server url-handlers www-root /myhugosite/public uri tcp://0.0.0.0/2345 cache-size 5m fifo-size 8192
The www-root must be specified, and must correctly name the compiled
hugo site root. If your Hugo site is located at /myhugosite, specify
diff --git a/extras/configs/http/setup.http b/extras/configs/http/setup.http
index 78b7a2f19e8..c4d4d1e6b70 100644
--- a/extras/configs/http/setup.http
+++ b/extras/configs/http/setup.http
@@ -3,5 +3,4 @@ create tap host-if-name lstack host-ip4-addr 192.168.10.2/24
set int ip address tap0 192.168.10.1/24
set int state tap0 up
-http static server www-root <path> uri tcp://0.0.0.0/1234 cache-size 10m fifo-size 2048
-builtinurl enable
+http static server url-handlers www-root <path> uri tcp://0.0.0.0/1234 cache-size 10m fifo-size 2048
diff --git a/extras/hs-test/Makefile b/extras/hs-test/Makefile
index 6ee89bc2770..2a501580e53 100644
--- a/extras/hs-test/Makefile
+++ b/extras/hs-test/Makefile
@@ -1,3 +1,9 @@
+export HS_ROOT=$(CURDIR)
+
+# sets WS_ROOT if called from extras/hs-test
+ifeq ($(WS_ROOT),)
+export WS_ROOT=$(HS_ROOT)/../..
+endif
ifeq ($(VERBOSE),)
VERBOSE=false
@@ -15,6 +21,10 @@ ifeq ($(TEST),)
TEST=all
endif
+ifeq ($(TEST-HS),)
+TEST-HS=all
+endif
+
ifeq ($(DEBUG),)
DEBUG=false
endif
@@ -31,6 +41,10 @@ ifeq ($(REPEAT),)
REPEAT=0
endif
+ifeq ($(CPU0),)
+CPU0=false
+endif
+
ifeq ($(VPPSRC),)
VPPSRC=$(shell pwd)/../..
endif
@@ -43,69 +57,92 @@ ifeq ($(ARCH),)
ARCH=$(shell dpkg --print-architecture)
endif
-list_tests = @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color --seed=2 | head -n -1 | grep 'Test' | \
- sed 's/^/* /; s/\(Suite\) /\1\//g'
-
.PHONY: help
help:
@echo "Make targets:"
- @echo " test - run tests"
- @echo " test-debug - run tests (vpp debug image)"
- @echo " build - build test infra"
- @echo " build-debug - build test infra (vpp debug image)"
- @echo " build-go - just build golang files"
- @echo " fixstyle - format .go source files"
- @echo " list-tests - list all tests"
+ @echo " test - run tests"
+ @echo " test-debug - run tests (vpp debug image)"
+ @echo " test-leak - run memory leak tests (vpp debug image)"
+ @echo " build - build test infra"
+ @echo " build-cov - coverage build of VPP and Docker images"
+ @echo " build-debug - build test infra (vpp debug image)"
+ @echo " build-go - just build golang files"
+ @echo " checkstyle-go - check style of .go source files"
+ @echo " fixstyle-go - format .go source files"
+ @echo " cleanup-hst - stops and removes all docker contaiers and namespaces"
+ @echo " list-tests - list all tests"
@echo
- @echo "make build arguments:"
+ @echo "'make build' arguments:"
@echo " UBUNTU_VERSION - ubuntu version for docker image"
- @echo " HST_EXTENDED_TESTS - build extended tests"
+ @echo " HST_EXTENDED_TESTS - build extended tests"
@echo
- @echo "make test arguments:"
+ @echo "'make test' arguments:"
@echo " PERSIST=[true|false] - whether clean up topology and dockers after test"
@echo " VERBOSE=[true|false] - verbose output"
@echo " UNCONFIGURE=[true|false] - unconfigure selected test"
@echo " DEBUG=[true|false] - attach VPP to GDB"
@echo " TEST=[test-name] - specific test to run"
- @echo " CPUS=[n-cpus] - number of cpus to run with vpp"
+ @echo " CPUS=[n-cpus] - number of cpus to allocate to VPP and containers"
@echo " VPPSRC=[path-to-vpp-src] - path to vpp source files (for gdb)"
@echo " PARALLEL=[n-cpus] - number of test processes to spawn to run in parallel"
@echo " REPEAT=[n] - repeat tests up to N times or until a failure occurs"
+ @echo " CPU0=[true|false] - use cpu0"
@echo
@echo "List of all tests:"
- $(call list_tests)
+ @$(MAKE) list-tests
.PHONY: list-tests
list-tests:
- $(call list_tests)
+ @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color --seed=2 | head -n -1 | grep 'Test' | \
+ sed 's/^/* /; s/\(Suite\) /\1\//g'
.PHONY: build-vpp-release
build-vpp-release:
- @make -C ../.. build-release
+ @$(MAKE) -C ../.. build-release
.PHONY: build-vpp-debug
build-vpp-debug:
- @make -C ../.. build
+ @$(MAKE) -C ../.. build
+
+.PHONY: build-vpp-gcov
+build-vpp-gcov:
+ @$(MAKE) -C ../.. build-vpp-gcov
.build.ok: build
@touch .build.ok
+.build.cov.ok: build-vpp-gcov
+ @touch .build.cov.ok
+
.build_debug.ok: build-debug
@touch .build.ok
.PHONY: test
test: .deps.ok .build.ok
- -bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
+ @bash ./hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
--unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
- --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT)
- $(call jq-summary)
- @bash ./script/compress.sh
+ --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) --cpu0=$(CPU0); \
+ ./script/compress.sh $$?
+
.PHONY: test-debug
test-debug: .deps.ok .build_debug.ok
- @bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
+ @bash ./hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
--unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
- --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT)
+ --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) --debug_build=true \
+ --cpu0=$(CPU0); \
+ ./script/compress.sh $$?
+
+.PHONY: test-cov
+test-cov: .deps.ok .build.cov.ok
+ @bash ./hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
+ --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST-HS) --cpus=$(CPUS) \
+ --vppsrc=$(VPPSRC) --cpu0=$(CPU0); \
+ ./script/compress.sh $$?
+
+.PHONY: test-leak
+test-leak: .deps.ok .build_debug.ok
+ @bash ./hs_test.sh --test=$(TEST) --debug_build=true --leak_check=true --vppsrc=$(VPPSRC)
.PHONY: build-go
build-go:
@@ -117,6 +154,12 @@ build: .deps.ok build-vpp-release build-go
bash ./script/build_hst.sh release
@touch .build.ok
+.PHONY: build-cov
+build-cov: .deps.ok build-vpp-gcov build-go
+ @rm -f .build.cov.ok
+ bash ./script/build_hst.sh gcov
+ @touch .build.cov.ok
+
.PHONY: build-debug
build-debug: .deps.ok build-vpp-debug build-go
@rm -f .build.ok
@@ -124,27 +167,66 @@ build-debug: .deps.ok build-vpp-debug build-go
@touch .build.ok
.deps.ok:
- @sudo make install-deps
+ @sudo $(MAKE) install-deps
.PHONY: install-deps
install-deps:
@rm -f .deps.ok
@apt-get update \
&& apt-get install -y apt-transport-https ca-certificates curl software-properties-common \
- apache2-utils wrk bridge-utils
+ apache2-utils wrk bridge-utils gpg
@if [ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ] ; then \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \
echo "deb [arch=$(ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(UBUNTU_CODENAME) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ; \
apt-get update; \
fi
+ @apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
@touch .deps.ok
-.PHONY: fixstyle
-fixstyle:
- @gofmt -w .
+.PHONY: checkstyle-go
+checkstyle-go:
+ @output=$$(gofmt -d $${WS_ROOT}); \
+ if [ -z "$$output" ]; then \
+ echo "*******************************************************************"; \
+ echo "Checkstyle OK."; \
+ echo "*******************************************************************"; \
+ else \
+ echo "$$output"; \
+ echo "*******************************************************************"; \
+ echo "Checkstyle failed. Use 'make fixstyle-go' or fix errors manually."; \
+ echo "*******************************************************************"; \
+ exit 1; \
+ fi
+
+.PHONY: fixstyle-go
+fixstyle-go:
+ @echo "Modified files:"
+ @gofmt -w -l $(WS_ROOT)
@go mod tidy
-
-# splitting this into multiple lines breaks the command
-jq-summary = @jq -r '.[0] | .SpecReports[] | select(.State == "failed") | select(.Failure != null) | "TestName: \(.LeafNodeText)\nMessage:\n\(.Failure.Message)\n Full Stack Trace:\n\(.Failure.Location.FullStackTrace)\n"' summary/report.json > summary/failed-summary.log \
- && echo "Summary generated -> failed-summary.log"
+ @echo "*******************************************************************"
+ @echo "Fixstyle done."
+ @echo "*******************************************************************"
+
+.PHONY: cleanup-hst
+cleanup-hst:
+ @if [ ! -f ".last_hst_ppid" ]; then \
+ echo "'.last_hst_ppid' file does not exist."; \
+ exit 1; \
+ fi
+ @echo "****************************"
+ @echo "Removing docker containers:"
+ @# "-" ignores errors
+ @-sudo docker rm $$(sudo docker stop $$(sudo docker ps -a -q --filter "name=$$(cat .last_hst_ppid)") -t 0)
+ @echo "****************************"
+ @echo "Removing IP address files:"
+ @find . -type f -regextype egrep -regex '.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' -exec sudo rm -v {} \;
+ @echo "****************************"
+ @echo "Removing network namespaces:"
+ @for ns in $$(ip netns list | grep $$(cat .last_hst_ppid) | awk '{print $$1}'); do \
+ echo $$ns; \
+ sudo ip netns delete $$ns; \
+ done
+ @echo "****************************"
+ @echo "Done."
+ @echo "****************************"
diff --git a/extras/hs-test/README.rst b/extras/hs-test/README.rst
index 1dc1039b33f..6a4cea187c4 100644
--- a/extras/hs-test/README.rst
+++ b/extras/hs-test/README.rst
@@ -26,15 +26,15 @@ Anatomy of a test case
**Action flow when running a test case**:
-#. It starts with running ``make test``. Optional arguments are VERBOSE, PERSIST (topology configuration isn't cleaned up after test run),
+#. It starts with running ``make test``. Optional arguments are VERBOSE, PERSIST (topology configuration isn't cleaned up after test run, use ``make cleanup-hst`` to clean up),
TEST=<test-name> to run a specific test and PARALLEL=[n-cpus].
-#. ``make list-tests`` (or ``make help``) shows all tests. The current `list of tests`_ is at the bottom of this document.
-#. ``Ginkgo`` looks for a spec suite in the current directory and then compiles it to a .test binary
-#. The Ginkgo test framework runs each function that was registered manually using ``registerMySuiteTest(s *MySuite)``. Each of these functions correspond to a suite
+#. ``make list-tests`` (or ``make help``) shows all tests.
+#. ``Ginkgo`` looks for a spec suite in the current directory and then compiles it to a .test binary.
+#. The Ginkgo test framework runs each function that was registered manually using ``Register[SuiteName]Test()``. Each of these functions correspond to a suite.
#. Ginkgo's ``RunSpecs(t, "Suite description")`` function is the entry point and does the following:
#. Ginkgo compiles the spec, builds a spec tree
- #. ``Describe`` container nodes in suite\_\*_test.go files are run (in series by default, or in parallel with the argument PARALLEL=[n-cpus])
+ #. ``Describe`` container nodes in suite\_\*.go files are run (in series by default, or in parallel with the argument PARALLEL=[n-cpus])
#. Suite is initialized. The topology is loaded and configured in this step
#. Registered tests are run in generated ``It`` subject nodes
#. Execute tear-down functions, which currently consists of stopping running containers
@@ -47,48 +47,78 @@ This describes adding a new test case to an existing suite.
For adding a new suite, please see `Modifying the framework`_ below.
#. To write a new test case, create a file whose name ends with ``_test.go`` or pick one that already exists
-#. Declare method whose name ends with ``Test`` and specifies its parameter as a pointer to the suite's struct (defined in ``suite_*_test.go``)
+#. Declare method whose name ends with ``Test`` and specifies its parameter as a pointer to the suite's struct (defined in ``infra/suite_*.go``)
#. Implement test behaviour inside the test method. This typically includes the following:
- #. Retrieve a running container in which to run some action. Method ``getContainerByName``
+ #. Import ``. "fd.io/hs-test/infra"``
+ #. Retrieve a running container in which to run some action. Method ``GetContainerByName``
from ``HstSuite`` struct serves this purpose
- #. Interact with VPP through the ``VppInstance`` struct embedded in container. It provides ``vppctl`` method to access debug CLI
- #. Run arbitrary commands inside the containers with ``exec`` method
- #. Run other external tool with one of the preexisting functions in the ``utils.go`` file.
- For example, use ``wget`` with ``startWget`` function
+ #. Interact with VPP through the ``VppInstance`` struct embedded in container. It provides ``Vppctl`` method to access debug CLI
+ #. Run arbitrary commands inside the containers with ``Exec`` method
+ #. Run other external tool with one of the preexisting functions in the ``infra/utils.go`` file.
+ For example, use ``wget`` with ``StartWget`` function
#. Use ``exechelper`` or just plain ``exec`` packages to run whatever else
- #. Verify results of your tests using ``assert`` methods provided by the test suite, implemented by HstSuite struct or use ``Gomega`` assert functions.
+ #. Verify results of your tests using ``Assert`` methods provided by the test suite.
-#. Create an ``init()`` function and register the test using ``register*SuiteTests(testCaseFunction)``
+#. Create an ``init()`` function and register the test using ``Register[SuiteName]Tests(testCaseFunction)``
**Example test case**
Assumed are two docker containers, each with its own VPP instance running. One VPP then pings the other.
-This can be put in file ``extras/hs-test/my_test.go`` and run with command ``make test TEST=MyTest`` or ``ginkgo -v --trace --focus MyTest``.
+This can be put in file ``extras/hs-test/my_test.go`` and run with command ``make test TEST=MyTest``.
::
package main
import (
- "fmt"
+ . "fd.io/hs-test/infra"
)
func init(){
- registerMySuiteTest(MyTest)
+ RegisterMySuiteTest(MyTest)
}
func MyTest(s *MySuite) {
- clientVpp := s.getContainerByName("client-vpp").vppInstance
+ clientVpp := s.GetContainerByName("client-vpp").VppInstance
- serverVethAddress := s.netInterfaces["server-iface"].AddressString()
+ serverVethAddress := s.NetInterfaces["server-iface"].Ip4AddressString()
- result := clientVpp.vppctl("ping " + serverVethAddress)
- s.assertNotNil(result)
- s.log(result)
+ result := clientVpp.Vppctl("ping " + serverVethAddress)
+ s.Log(result)
+ s.AssertNotNil(result)
}
+
+Filtering test cases
+--------------------
+
+The framework allows us to filter test cases in a few different ways, using ``make test TEST=``:
+
+ * Suite name
+ * File name
+ * Test name
+ * All of the above as long as they are ordered properly, e.g. ``make test TEST=VethsSuite.http_test.go.HeaderServerTest``
+
+**Names are case sensitive!**
+
+Names don't have to be complete, as long as they are last:
+This is valid and will run all tests in every ``http`` file (if there is more than one):
+
+* ``make test TEST=VethsSuite.http``
+
+This is not valid:
+
+* ``make test TEST=Veths.http``
+
+They can also be left out:
+
+* ``make test TEST=http_test.go`` will run every test in ``http_test.go``
+* ``make test TEST=Nginx`` will run everything that has 'Nginx' in its name - suites, files and tests.
+* ``make test TEST=HeaderServerTest`` will only run the header server test
+
+
Modifying the framework
-----------------------
@@ -96,34 +126,37 @@ Modifying the framework
.. _test-convention:
-#. To add a new suite, create a new file. Naming convention for the suite files is ``suite_name_test.go`` where *name* will be replaced
- by the actual name
+#. To add a new suite, create a new file in the ``infra/`` folder. Naming convention for the suite files is ``suite_[name].go``.
#. Make a ``struct``, in the suite file, with at least ``HstSuite`` struct as its member.
HstSuite provides functionality that can be shared for all suites, like starting containers
+#. Create a new map that will contain a file name where a test is located and test functions with a pointer to the suite's struct: ``var myTests = map[string][]func(s *MySuite){}``
+
::
+ var myTests = map[string][]func(s *MySuite){}
+
type MySuite struct {
HstSuite
}
-#. Create a new slice that will contain test functions with a pointer to the suite's struct: ``var myTests = []func(s *MySuite){}``
-#. Then create a new function that will append test functions to that slice:
+#. Then create a new function that will add tests to that map:
::
- func registerMySuiteTests(tests ...func(s *MySuite)) {
- nginxTests = append(myTests, tests...)
+ func RegisterMyTests(tests ...func(s *MySuite)) {
+ myTests[getTestFilename()] = tests
}
+
#. In suite file, implement ``SetupSuite`` method which Ginkgo runs once before starting any of the tests.
- It's important here to call ``configureNetworkTopology`` method,
+ It's important here to call ``ConfigureNetworkTopology()`` method,
pass the topology name to the function in a form of file name of one of the *yaml* files in ``topo-network`` folder.
Without the extension. In this example, *myTopology* corresponds to file ``extras/hs-test/topo-network/myTopology.yaml``
This will ensure network topology, such as network interfaces and namespaces, will be created.
- Another important method to call is ``loadContainerTopology()`` which will load
+ Another important method to call is ``LoadContainerTopology()`` which will load
containers and shared volumes used by the suite. This time the name passed to method corresponds
to file in ``extras/hs-test/topo-containers`` folder
@@ -134,8 +167,8 @@ Modifying the framework
// Add custom setup code here
- s.configureNetworkTopology("myTopology")
- s.loadContainerTopology("2peerVeth")
+ s.ConfigureNetworkTopology("myTopology")
+ s.LoadContainerTopology("2peerVeth")
}
#. In suite file, implement ``SetupTest`` method which gets executed before each test. Starting containers and
@@ -160,44 +193,50 @@ Modifying the framework
::
var _ = Describe("MySuite", Ordered, ContinueOnFailure, func() {
- var s MySuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
+ var s MySuite
+ BeforeAll(func() {
+ s.SetupSuite()
})
- for _, test := range mySuiteTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) {
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range myTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
})
#. Notice the loop - it will generate multiple ``It`` nodes, each running a different test.
``test := test`` is necessary, otherwise only the last test in a suite will run.
For a more detailed description, check Ginkgo's documentation: https://onsi.github.io/ginkgo/#dynamically-generating-specs\.
-#. ``funcValue.Name()`` returns the full name of a function (e.g. ``fd.io/hs-test.MyTest``), however, we only need the test name (``MyTest``).
+#. ``testName`` contains the test name in the following format: ``[name]_test.go/MyTest``.
-#. To run certain tests solo, create a new slice that will only contain tests that have to run solo and a new register function.
+#. To run certain tests solo, create a register function and a map that will only contain tests that have to run solo.
Add a ``Serial`` decorator to the container node and ``Label("SOLO")`` to the ``It`` subject node:
::
var _ = Describe("MySuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
...
- It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) {
- test(&s)
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
}, SpecTimeout(time.Minute*5))
})
@@ -254,7 +293,23 @@ Alternatively copy the executable from host system to the Docker image, similarl
However the tests currently run under test suites which set up topology and containers before actual test is run. For the reason of saving
test run time it is not advisable to use aforementioned skip methods and instead, just don't register the test.
-**Debugging a test**
+**External dependencies**
+
+* Linux tools ``ip``, ``brctl``
+* Standalone programs ``wget``, ``iperf3`` - since these are downloaded when Docker image is made,
+ they are reasonably up-to-date automatically
+* Programs in Docker images - ``envoyproxy/envoy-contrib`` and ``nginx``
+* ``http_server`` - homegrown application that listens on specified port and sends a test file in response
+* Non-standard Go libraries - see ``extras/hs-test/go.mod``
+
+Generally, these will be updated on a per-need basis, for example when a bug is discovered
+or a new version incompatibility issue occurs.
+
+Debugging a test
+----------------
+
+GDB
+^^^
It is possible to debug VPP by attaching ``gdb`` before test execution by adding ``DEBUG=true`` like follows:
@@ -268,52 +323,71 @@ It is possible to debug VPP by attaching ``gdb`` before test execution by adding
If a test consists of more VPP instances then this is done for each of them.
+Utility methods
+^^^^^^^^^^^^^^^
-**Eternal dependencies**
+**Packet Capture**
-* Linux tools ``ip``, ``brctl``
-* Standalone programs ``wget``, ``iperf3`` - since these are downloaded when Docker image is made,
- they are reasonably up-to-date automatically
-* Programs in Docker images - ``envoyproxy/envoy-contrib`` and ``nginx``
-* ``http_server`` - homegrown application that listens on specified port and sends a test file in response
-* Non-standard Go libraries - see ``extras/hs-test/go.mod``
+It is possible to use VPP pcap trace to capture received and sent packets.
+You just need to add ``EnablePcapTrace`` to ``SetupTest`` method in test suite and ``CollectPcapTrace`` to ``TearDownTest``.
+This way pcap trace is enabled on all interfaces and to capture maximum 10000 packets.
+Your pcap file will be located in the test execution directory.
-Generally, these will be updated on a per-need basis, for example when a bug is discovered
-or a new version incompatibility issue occurs.
+**Event Logger**
+
+``clib_warning`` is a handy way to add debugging output, but in some cases it's not appropriate for per-packet use in data plane code.
+In this case VPP event logger is better option, for example you can enable it for TCP or session layer in build time.
+To collect traces when test ends you just need to add ``CollectEventLogs`` method to ``TearDownTest`` in the test suite.
+Your event logger file will be located in the test execution directory.
+To view events you can use :ref:`G2 graphical event viewer <eventviewer>` or ``convert_evt`` tool, located in ``src/scripts/host-stack/``,
+which convert event logs to human readable text.
+
+Memory leak testing
+^^^^^^^^^^^^^^^^^^^
+
+It is possible to use VPP memory traces to diagnose if and where memory leaks happen by comparing of two traces at different point in time.
+You can do it by test like following:
+
+::
+
+ func MemLeakTest(s *NoTopoSuite) {
+ s.SkipUnlessLeakCheck() // test is excluded from usual test run
+ vpp := s.GetContainerByName("vpp").VppInstance
+ /* do your configuration here */
+ vpp.Disconnect() // no goVPP less noise
+ vpp.EnableMemoryTrace() // enable memory traces
+ traces1, err := vpp.GetMemoryTrace() // get first sample
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.Vppctl("test mem-leak") // execute some action
+ traces2, err := vpp.GetMemoryTrace() // get second sample
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.MemLeakCheck(traces1, traces2) // compare samples and generate report
+ }
+
+To get your memory leak report run following command:
+
+::
+
+ $ make test-leak TEST=MemLeakTest
+ ...
+ NoTopoSuiteSolo mem_leak_test.go/MemLeakTest [SOLO]
+ /home/matus/vpp/extras/hs-test/infra/suite_no_topo.go:113
+
+ Report Entries >>
+
+ SUMMARY: 112 byte(s) leaked in 1 allocation(s)
+ - /home/matus/vpp/extras/hs-test/infra/vppinstance.go:624 @ 07/19/24 15:53:33.539
+
+ leak of 112 byte(s) in 1 allocation(s) from:
+ #0 clib_mem_heap_alloc_aligned + 0x31
+ #1 _vec_alloc_internal + 0x113
+ #2 _vec_validate + 0x81
+ #3 leak_memory_fn + 0x4f
+ #4 0x7fc167815ac3
+ #5 0x7fc1678a7850
+ << Report Entries
+ ------------------------------
.. _ginkgo: https://onsi.github.io/ginkgo/
.. _volumes: https://docs.docker.com/storage/volumes/
-
-**List of tests**
-
-.. _list of tests:
-
-Please update this list whenever you add a new test by pasting the output below.
-
-* NsSuite/HttpTpsTest
-* NsSuite/VppProxyHttpTcpTest
-* NsSuite/VppProxyHttpTlsTest
-* NsSuite/EnvoyProxyHttpTcpTest
-* NginxSuite/MirroringTest
-* VethsSuiteSolo TcpWithLossTest [SOLO]
-* NoTopoSuiteSolo HttpStaticPromTest [SOLO]
-* TapSuite/LinuxIperfTest
-* NoTopoSuite/NginxHttp3Test
-* NoTopoSuite/NginxAsServerTest
-* NoTopoSuite/NginxPerfCpsTest
-* NoTopoSuite/NginxPerfRpsTest
-* NoTopoSuite/NginxPerfWrkTest
-* VethsSuite/EchoBuiltinTest
-* VethsSuite/HttpCliTest
-* VethsSuite/LDPreloadIperfVppTest
-* VethsSuite/VppEchoQuicTest
-* VethsSuite/VppEchoTcpTest
-* VethsSuite/VppEchoUdpTest
-* VethsSuite/XEchoVclClientUdpTest
-* VethsSuite/XEchoVclClientTcpTest
-* VethsSuite/XEchoVclServerUdpTest
-* VethsSuite/XEchoVclServerTcpTest
-* VethsSuite/VclEchoTcpTest
-* VethsSuite/VclEchoUdpTest
-* VethsSuite/VclRetryAttachTest
diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go
deleted file mode 100644
index 45f41d26a87..00000000000
--- a/extras/hs-test/container.go
+++ /dev/null
@@ -1,373 +0,0 @@
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "strings"
- "text/template"
- "time"
-
- "github.com/edwarnicke/exechelper"
- . "github.com/onsi/ginkgo/v2"
-)
-
-const (
- logDir string = "/tmp/hs-test/"
- volumeDir string = "/volumes"
-)
-
-var (
- workDir, _ = os.Getwd()
-)
-
-type Volume struct {
- hostDir string
- containerDir string
- isDefaultWorkDir bool
-}
-
-type Container struct {
- suite *HstSuite
- isOptional bool
- runDetached bool
- name string
- image string
- extraRunningArgs string
- volumes map[string]Volume
- envVars map[string]string
- vppInstance *VppInstance
-}
-
-func newContainer(suite *HstSuite, yamlInput ContainerConfig) (*Container, error) {
- containerName := yamlInput["name"].(string)
- if len(containerName) == 0 {
- err := fmt.Errorf("container name must not be blank")
- return nil, err
- }
-
- var container = new(Container)
- container.volumes = make(map[string]Volume)
- container.envVars = make(map[string]string)
- container.name = containerName
- container.suite = suite
-
- if image, ok := yamlInput["image"]; ok {
- container.image = image.(string)
- } else {
- container.image = "hs-test/vpp"
- }
-
- if args, ok := yamlInput["extra-args"]; ok {
- container.extraRunningArgs = args.(string)
- } else {
- container.extraRunningArgs = ""
- }
-
- if isOptional, ok := yamlInput["is-optional"]; ok {
- container.isOptional = isOptional.(bool)
- } else {
- container.isOptional = false
- }
-
- if runDetached, ok := yamlInput["run-detached"]; ok {
- container.runDetached = runDetached.(bool)
- } else {
- container.runDetached = true
- }
-
- if _, ok := yamlInput["volumes"]; ok {
- workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + volumeDir
- workDirReplacer := strings.NewReplacer("$HST_DIR", workDir)
- volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir)
- for _, volu := range yamlInput["volumes"].([]interface{}) {
- volumeMap := volu.(ContainerConfig)
- hostDir := workDirReplacer.Replace(volumeMap["host-dir"].(string))
- hostDir = volDirReplacer.Replace(hostDir)
- containerDir := volumeMap["container-dir"].(string)
- isDefaultWorkDir := false
-
- if isDefault, ok := volumeMap["is-default-work-dir"]; ok {
- isDefaultWorkDir = isDefault.(bool)
- }
- container.addVolume(hostDir, containerDir, isDefaultWorkDir)
- }
- }
-
- if _, ok := yamlInput["vars"]; ok {
- for _, envVar := range yamlInput["vars"].([]interface{}) {
- envVarMap := envVar.(ContainerConfig)
- name := envVarMap["name"].(string)
- value := envVarMap["value"].(string)
- container.addEnvVar(name, value)
- }
- }
- return container, nil
-}
-
-func (c *Container) getWorkDirVolume() (res Volume, exists bool) {
- for _, v := range c.volumes {
- if v.isDefaultWorkDir {
- res = v
- exists = true
- return
- }
- }
- return
-}
-
-func (c *Container) getHostWorkDir() (res string) {
- if v, ok := c.getWorkDirVolume(); ok {
- res = v.hostDir
- }
- return
-}
-
-func (c *Container) getContainerWorkDir() (res string) {
- if v, ok := c.getWorkDirVolume(); ok {
- res = v.containerDir
- }
- return
-}
-
-func (c *Container) getContainerArguments() string {
- args := "--ulimit nofile=90000:90000 --cap-add=all --privileged --network host --rm"
- args += c.getVolumesAsCliOption()
- args += c.getEnvVarsAsCliOption()
- if *vppSourceFileDir != "" {
- args += fmt.Sprintf(" -v %s:%s", *vppSourceFileDir, *vppSourceFileDir)
- }
- args += " --name " + c.name + " " + c.image
- args += " " + c.extraRunningArgs
- return args
-}
-
-func (c *Container) runWithRetry(cmd string) error {
- nTries := 5
- for i := 0; i < nTries; i++ {
- err := exechelper.Run(cmd)
- if err == nil {
- return nil
- }
- time.Sleep(1 * time.Second)
- }
- return fmt.Errorf("failed to run container command")
-}
-
-func (c *Container) create() error {
- cmd := "docker create " + c.getContainerArguments()
- c.suite.log(cmd)
- return exechelper.Run(cmd)
-}
-
-func (c *Container) start() error {
- cmd := "docker start " + c.name
- c.suite.log(cmd)
- return c.runWithRetry(cmd)
-}
-
-func (c *Container) prepareCommand() (string, error) {
- if c.name == "" {
- return "", fmt.Errorf("run container failed: name is blank")
- }
-
- cmd := "docker run "
- if c.runDetached {
- cmd += " -d"
- }
- cmd += " " + c.getContainerArguments()
-
- c.suite.log(cmd)
- return cmd, nil
-}
-
-func (c *Container) combinedOutput() (string, error) {
- cmd, err := c.prepareCommand()
- if err != nil {
- return "", err
- }
-
- byteOutput, err := exechelper.CombinedOutput(cmd)
- return string(byteOutput), err
-}
-
-func (c *Container) run() error {
- cmd, err := c.prepareCommand()
- if err != nil {
- return err
- }
- return c.runWithRetry(cmd)
-}
-
-func (c *Container) addVolume(hostDir string, containerDir string, isDefaultWorkDir bool) {
- var volume Volume
- volume.hostDir = hostDir
- volume.containerDir = containerDir
- volume.isDefaultWorkDir = isDefaultWorkDir
- c.volumes[hostDir] = volume
-}
-
-func (c *Container) getVolumesAsCliOption() string {
- cliOption := ""
-
- if len(c.volumes) > 0 {
- for _, volume := range c.volumes {
- cliOption += fmt.Sprintf(" -v %s:%s", volume.hostDir, volume.containerDir)
- }
- }
-
- return cliOption
-}
-
-func (c *Container) addEnvVar(name string, value string) {
- c.envVars[name] = value
-}
-
-func (c *Container) getEnvVarsAsCliOption() string {
- cliOption := ""
- if len(c.envVars) == 0 {
- return cliOption
- }
-
- for name, value := range c.envVars {
- cliOption += fmt.Sprintf(" -e %s=%s", name, value)
- }
- return cliOption
-}
-
-func (c *Container) newVppInstance(cpus []int, additionalConfigs ...Stanza) (*VppInstance, error) {
- vpp := new(VppInstance)
- vpp.container = c
- vpp.cpus = cpus
- vpp.additionalConfig = append(vpp.additionalConfig, additionalConfigs...)
- c.vppInstance = vpp
- return vpp, nil
-}
-
-func (c *Container) copy(sourceFileName string, targetFileName string) error {
- cmd := exec.Command("docker", "cp", sourceFileName, c.name+":"+targetFileName)
- return cmd.Run()
-}
-
-func (c *Container) createFile(destFileName string, content string) error {
- f, err := os.CreateTemp("/tmp", "hst-config"+c.suite.pid)
- if err != nil {
- return err
- }
- defer os.Remove(f.Name())
-
- if _, err := f.Write([]byte(content)); err != nil {
- return err
- }
- if err := f.Close(); err != nil {
- return err
- }
- c.copy(f.Name(), destFileName)
- return nil
-}
-
-/*
- * Executes in detached mode so that the started application can continue to run
- * without blocking execution of test
- */
-func (c *Container) execServer(command string, arguments ...any) {
- serverCommand := fmt.Sprintf(command, arguments...)
- containerExecCommand := "docker exec -d" + c.getEnvVarsAsCliOption() +
- " " + c.name + " " + serverCommand
- GinkgoHelper()
- c.suite.log(containerExecCommand)
- c.suite.assertNil(exechelper.Run(containerExecCommand))
-}
-
-func (c *Container) exec(command string, arguments ...any) string {
- cliCommand := fmt.Sprintf(command, arguments...)
- containerExecCommand := "docker exec" + c.getEnvVarsAsCliOption() +
- " " + c.name + " " + cliCommand
- GinkgoHelper()
- c.suite.log(containerExecCommand)
- byteOutput, err := exechelper.CombinedOutput(containerExecCommand)
- c.suite.assertNil(err, err)
- return string(byteOutput)
-}
-
-func (c *Container) getLogDirPath() string {
- testId := c.suite.getTestId()
- testName := CurrentSpecReport().LeafNodeText
- logDirPath := logDir + testName + "/" + testId + "/"
-
- cmd := exec.Command("mkdir", "-p", logDirPath)
- if err := cmd.Run(); err != nil {
- Fail("mkdir error: " + fmt.Sprint(err))
- }
-
- return logDirPath
-}
-
-func (c *Container) saveLogs() {
- cmd := exec.Command("docker", "inspect", "--format='{{.State.Status}}'", c.name)
- if output, _ := cmd.CombinedOutput(); !strings.Contains(string(output), "running") {
- return
- }
-
- testLogFilePath := c.getLogDirPath() + "container-" + c.name + ".log"
-
- cmd = exec.Command("docker", "logs", "--details", "-t", c.name)
- output, err := cmd.CombinedOutput()
- if err != nil {
- Fail("fetching logs error: " + fmt.Sprint(err))
- }
-
- f, err := os.Create(testLogFilePath)
- if err != nil {
- Fail("file create error: " + fmt.Sprint(err))
- }
- fmt.Fprint(f, string(output))
- f.Close()
-}
-
-// Outputs logs from docker containers. Set 'maxLines' to 0 to output the full log.
-func (c *Container) log(maxLines int) (string, error) {
- var cmd string
- if maxLines == 0 {
- cmd = "docker logs " + c.name
- } else {
- cmd = fmt.Sprintf("docker logs --tail %d %s", maxLines, c.name)
- }
-
- c.suite.log(cmd)
- o, err := exechelper.CombinedOutput(cmd)
- return string(o), err
-}
-
-func (c *Container) stop() error {
- if c.vppInstance != nil && c.vppInstance.apiChannel != nil {
- c.vppInstance.saveLogs()
- c.vppInstance.disconnect()
- }
- c.vppInstance = nil
- c.saveLogs()
- return exechelper.Run("docker stop " + c.name + " -t 0")
-}
-
-func (c *Container) createConfig(targetConfigName string, templateName string, values any) {
- template := template.Must(template.ParseFiles(templateName))
-
- f, err := os.CreateTemp(logDir, "hst-config")
- c.suite.assertNil(err, err)
- defer os.Remove(f.Name())
-
- err = template.Execute(f, values)
- c.suite.assertNil(err, err)
-
- err = f.Close()
- c.suite.assertNil(err, err)
-
- c.copy(f.Name(), targetConfigName)
-}
-
-func init() {
- cmd := exec.Command("mkdir", "-p", logDir)
- if err := cmd.Run(); err != nil {
- panic(err)
- }
-}
diff --git a/extras/hs-test/cpu.go b/extras/hs-test/cpu.go
deleted file mode 100644
index a976f47d8a5..00000000000
--- a/extras/hs-test/cpu.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package main
-
-import (
- "bufio"
- "errors"
- "fmt"
- "os"
- "os/exec"
- "strings"
-)
-
-var CgroupPath = "/sys/fs/cgroup/"
-
-type CpuContext struct {
- cpuAllocator *CpuAllocatorT
- cpus []int
-}
-
-func (c *CpuContext) Release() {
- c.cpuAllocator.cpus = append(c.cpuAllocator.cpus, c.cpus...)
- c.cpus = c.cpus[:0] // empty the list
-}
-
-type CpuAllocatorT struct {
- cpus []int
-}
-
-var cpuAllocator *CpuAllocatorT = nil
-
-func (c *CpuAllocatorT) Allocate(nCpus int) (*CpuContext, error) {
- var cpuCtx CpuContext
-
- if len(c.cpus) < nCpus {
- return nil, fmt.Errorf("could not allocate %d CPUs; available: %d", nCpus, len(c.cpus))
- }
- cpuCtx.cpus = c.cpus[0:nCpus]
- cpuCtx.cpuAllocator = c
- c.cpus = c.cpus[nCpus:]
- return &cpuCtx, nil
-}
-
-func (c *CpuAllocatorT) readCpus() error {
- var first, last int
-
- // Path depends on cgroup version. We need to check which version is in use.
- // For that following command can be used: 'stat -fc %T /sys/fs/cgroup/'
- // In case the output states 'cgroup2fs' then cgroups v2 is used, 'tmpfs' in case cgroups v1.
- cmd := exec.Command("stat", "-fc", "%T", "/sys/fs/cgroup/")
- byteOutput, err := cmd.CombinedOutput()
- if err != nil {
- return err
- }
- CpuPath := CgroupPath
- if strings.Contains(string(byteOutput), "tmpfs") {
- CpuPath += "cpuset/cpuset.effective_cpus"
- } else if strings.Contains(string(byteOutput), "cgroup2fs") {
- CpuPath += "cpuset.cpus.effective"
- } else {
- return errors.New("cgroup unknown fs: " + string(byteOutput))
- }
-
- file, err := os.Open(CpuPath)
- if err != nil {
- return err
- }
- defer file.Close()
-
- sc := bufio.NewScanner(file)
- sc.Scan()
- line := sc.Text()
- _, err = fmt.Sscanf(line, "%d-%d", &first, &last)
- if err != nil {
- return err
- }
- for i := first; i <= last; i++ {
- c.cpus = append(c.cpus, i)
- }
- return nil
-}
-
-func CpuAllocator() (*CpuAllocatorT, error) {
- if cpuAllocator == nil {
- cpuAllocator = new(CpuAllocatorT)
- err := cpuAllocator.readCpus()
- if err != nil {
- return nil, err
- }
- }
- return cpuAllocator, nil
-}
diff --git a/extras/hs-test/cpu_pinning_test.go b/extras/hs-test/cpu_pinning_test.go
new file mode 100644
index 00000000000..b8dec65937e
--- /dev/null
+++ b/extras/hs-test/cpu_pinning_test.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ . "fd.io/hs-test/infra"
+)
+
+func init() {
+ RegisterCpuPinningSoloTests(DefaultCpuConfigurationTest, SkipCoresTest)
+}
+
+// TODO: Add more CPU configuration tests
+
+func DefaultCpuConfigurationTest(s *CpuPinningSuite) {
+ vpp := s.GetContainerByName(SingleTopoContainerVpp).VppInstance
+ s.AssertNil(vpp.Start())
+}
+
+func SkipCoresTest(s *CpuPinningSuite) {
+
+ skipCoresConfiguration := VppCpuConfig{
+ PinMainCpu: true,
+ PinWorkersCorelist: true,
+ SkipCores: 1,
+ }
+
+ vpp := s.GetContainerByName(SingleTopoContainerVpp).VppInstance
+ vpp.CpuConfig = skipCoresConfiguration
+
+ s.AssertNil(vpp.Start())
+}
diff --git a/extras/hs-test/docker/Dockerfile.build b/extras/hs-test/docker/Dockerfile.build
deleted file mode 100644
index 8b2652e93fc..00000000000
--- a/extras/hs-test/docker/Dockerfile.build
+++ /dev/null
@@ -1,8 +0,0 @@
-ARG UBUNTU_VERSION
-
-FROM ubuntu:${UBUNTU_VERSION}
-
-RUN apt-get update \
- && apt-get install -y gcc git make autoconf libtool pkg-config cmake ninja-build golang \
- && rm -rf /var/lib/apt/lists/*
-
diff --git a/extras/hs-test/docker/Dockerfile.curl b/extras/hs-test/docker/Dockerfile.curl
index 81d15e86c82..cbb0bbe734f 100644
--- a/extras/hs-test/docker/Dockerfile.curl
+++ b/extras/hs-test/docker/Dockerfile.curl
@@ -1,6 +1,14 @@
-FROM hs-test/build
+ARG UBUNTU_VERSION
+
+FROM ubuntu:${UBUNTU_VERSION}
+
+RUN apt-get update \
+ && apt-get install -y gcc git make autoconf libtool pkg-config cmake ninja-build golang \
+ && rm -rf /var/lib/apt/lists/*
COPY script/build_curl.sh /build_curl.sh
+COPY resources/curl/* /tmp/
+RUN fallocate -l 10MB /tmp/testFile
RUN apt-get update && apt-get install wget
RUN /build_curl.sh
diff --git a/extras/hs-test/docker/Dockerfile.nginx b/extras/hs-test/docker/Dockerfile.nginx
index 11ec6af156d..386f8e90016 100644
--- a/extras/hs-test/docker/Dockerfile.nginx
+++ b/extras/hs-test/docker/Dockerfile.nginx
@@ -3,7 +3,7 @@ ARG UBUNTU_VERSION
FROM ubuntu:${UBUNTU_VERSION}
RUN apt-get update \
- && apt-get install -y nginx gdb less \
+ && apt-get install -y nginx gdb less libunwind-dev \
&& rm -rf /var/lib/apt/lists/*
COPY vpp-data/lib/* /usr/lib/
diff --git a/extras/hs-test/docker/Dockerfile.nginx-http3 b/extras/hs-test/docker/Dockerfile.nginx-http3
index 5d66a2528a6..8b2f8406d38 100644
--- a/extras/hs-test/docker/Dockerfile.nginx-http3
+++ b/extras/hs-test/docker/Dockerfile.nginx-http3
@@ -1,4 +1,10 @@
-FROM hs-test/build
+ARG UBUNTU_VERSION
+
+FROM ubuntu:${UBUNTU_VERSION}
+
+RUN apt-get update \
+ && apt-get install -y gcc git make autoconf libtool pkg-config cmake ninja-build golang \
+ && rm -rf /var/lib/apt/lists/*
COPY script/build_boringssl.sh /build_boringssl.sh
RUN git clone https://boringssl.googlesource.com/boringssl
diff --git a/extras/hs-test/docker/Dockerfile.nginx-server b/extras/hs-test/docker/Dockerfile.nginx-server
index 1971158131b..ecb8f590f89 100644
--- a/extras/hs-test/docker/Dockerfile.nginx-server
+++ b/extras/hs-test/docker/Dockerfile.nginx-server
@@ -7,6 +7,10 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*
COPY resources/nginx/nginx_server_mirroring.conf /nginx.conf
+COPY script/nginx_server_entrypoint.sh /usr/bin/nginx_server_entrypoint.sh
+COPY resources/nginx/html/index.html /usr/share/nginx/index.html
+RUN fallocate -l 10MB /usr/share/nginx/httpTestFile
+RUN mkdir /usr/share/nginx/upload && chmod 777 /usr/share/nginx/upload
-ENTRYPOINT ["nginx", "-c", "/nginx.conf"]
+ENTRYPOINT ["nginx_server_entrypoint.sh"]
diff --git a/extras/hs-test/docker/Dockerfile.vpp b/extras/hs-test/docker/Dockerfile.vpp
index ace83c593c6..82a1a1a73d3 100644
--- a/extras/hs-test/docker/Dockerfile.vpp
+++ b/extras/hs-test/docker/Dockerfile.vpp
@@ -5,7 +5,7 @@ FROM ubuntu:${UBUNTU_VERSION}
RUN apt-get update \
&& apt-get install -y openssl libapr1 libnuma1 libsubunit0 \
iproute2 libnl-3-dev libnl-route-3-dev python3 iputils-ping \
- vim gdb \
+ vim gdb libunwind-dev redis redis-tools iperf3 \
&& rm -rf /var/lib/apt/lists/*
ENV DIR=vpp-data/lib/vpp_plugins
@@ -20,6 +20,7 @@ COPY \
$DIR/nsim_plugin.so \
$DIR/prom_plugin.so \
$DIR/tlsopenssl_plugin.so \
+ $DIR/mactime_plugin.so \
/usr/lib/x86_64-linux-gnu/vpp_plugins/
COPY vpp-data/bin/vpp /usr/bin/
diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go
index 0515b5e0411..6b4739a5457 100644
--- a/extras/hs-test/echo_test.go
+++ b/extras/hs-test/echo_test.go
@@ -1,49 +1,56 @@
package main
+import (
+ . "fd.io/hs-test/infra"
+)
+
func init() {
- registerVethTests(EchoBuiltinTest)
- registerSoloVethTests(TcpWithLossTest)
+ RegisterVethTests(EchoBuiltinTest)
+ RegisterSoloVethTests(TcpWithLossTest)
}
func EchoBuiltinTest(s *VethsSuite) {
- serverVpp := s.getContainerByName("server-vpp").vppInstance
- serverVeth := s.getInterfaceByName(serverInterfaceName)
+ serverVpp := s.GetContainerByName("server-vpp").VppInstance
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
- serverVpp.vppctl("test echo server " +
- " uri tcp://" + serverVeth.ip4AddressString() + "/1234")
+ serverVpp.Vppctl("test echo server " +
+ " uri tcp://" + serverVeth.Ip4AddressString() + "/1234")
- clientVpp := s.getContainerByName("client-vpp").vppInstance
+ clientVpp := s.GetContainerByName("client-vpp").VppInstance
- o := clientVpp.vppctl("test echo client nclients 100 bytes 1 verbose" +
+ o := clientVpp.Vppctl("test echo client nclients 100 bytes 1 verbose" +
" syn-timeout 100 test-timeout 100" +
- " uri tcp://" + serverVeth.ip4AddressString() + "/1234")
- s.log(o)
- s.assertNotContains(o, "failed:")
+ " uri tcp://" + serverVeth.Ip4AddressString() + "/1234")
+ s.Log(o)
+ s.AssertNotContains(o, "failed:")
}
+// unstable with multiple workers
func TcpWithLossTest(s *VethsSuite) {
- serverVpp := s.getContainerByName("server-vpp").vppInstance
+ s.SkipIfMultiWorker()
+ serverVpp := s.GetContainerByName("server-vpp").VppInstance
- serverVeth := s.getInterfaceByName(serverInterfaceName)
- serverVpp.vppctl("test echo server uri tcp://%s/20022",
- serverVeth.ip4AddressString())
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ serverVpp.Vppctl("test echo server uri tcp://%s/20022",
+ serverVeth.Ip4AddressString())
- clientVpp := s.getContainerByName("client-vpp").vppInstance
+ clientVpp := s.GetContainerByName("client-vpp").VppInstance
// Ensure that VPP doesn't abort itself with NSIM enabled
// Warning: Removing this ping will make VPP crash!
- clientVpp.vppctl("ping %s", serverVeth.ip4AddressString())
+ clientVpp.Vppctl("ping %s", serverVeth.Ip4AddressString())
// Add loss of packets with Network Delay Simulator
- clientVpp.vppctl("set nsim poll-main-thread delay 0.01 ms bandwidth 40 gbit" +
+ clientVpp.Vppctl("set nsim poll-main-thread delay 0.01 ms bandwidth 40 gbit" +
" packet-size 1400 packets-per-drop 1000")
- clientVpp.vppctl("nsim output-feature enable-disable host-" + s.getInterfaceByName(clientInterfaceName).name)
+ name := s.GetInterfaceByName(ClientInterfaceName).Name()
+ clientVpp.Vppctl("nsim output-feature enable-disable host-" + name)
// Do echo test from client-vpp container
- output := clientVpp.vppctl("test echo client uri tcp://%s/20022 verbose echo-bytes mbytes 50",
- serverVeth.ip4AddressString())
- s.log(output)
- s.assertNotEqual(len(output), 0)
- s.assertNotContains(output, "failed: timeout", output)
+ output := clientVpp.Vppctl("test echo client uri tcp://%s/20022 verbose echo-bytes mbytes 50",
+ serverVeth.Ip4AddressString())
+ s.Log(output)
+ s.AssertNotEqual(len(output), 0)
+ s.AssertNotContains(output, "failed", output)
}
diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go
index 8773fa2417d..91cb1032bba 100644
--- a/extras/hs-test/framework_test.go
+++ b/extras/hs-test/framework_test.go
@@ -1,13 +1,36 @@
package main
import (
+ "fmt"
+ "os"
+ "strings"
"testing"
+ "time"
+ . "fd.io/hs-test/infra"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestHst(t *testing.T) {
+ if *IsVppDebug {
+ // 30 minute timeout so that the framework won't timeout while debugging
+ SuiteTimeout = time.Minute * 30
+ } else {
+ SuiteTimeout = time.Minute * 5
+ }
+
+ output, err := os.ReadFile("/sys/devices/system/node/online")
+ if err == nil && strings.Contains(string(output), "-") {
+ NumaAwareCpuAlloc = true
+ }
+ // creates a file with PPID, used for 'make cleanup-hst'
+ ppid := fmt.Sprint(os.Getppid())
+ ppid = ppid[:len(ppid)-1]
+ f, _ := os.Create(".last_hst_ppid")
+ f.Write([]byte(ppid))
+ f.Close()
+
RegisterFailHandler(Fail)
RunSpecs(t, "HST")
}
diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod
index 0f3854d2148..01cb9000bdc 100644
--- a/extras/hs-test/go.mod
+++ b/extras/hs-test/go.mod
@@ -1,33 +1,67 @@
module fd.io/hs-test
-go 1.21
+go 1.22.5
require (
+ github.com/cilium/cilium v1.15.7
+ github.com/docker/docker v27.1.1+incompatible
+ github.com/docker/go-units v0.5.0
github.com/edwarnicke/exechelper v1.0.3
+ github.com/onsi/ginkgo/v2 v2.17.2
+ github.com/onsi/gomega v1.33.1
+ github.com/sirupsen/logrus v1.9.3
go.fd.io/govpp v0.10.0
gopkg.in/yaml.v3 v3.0.1
)
require (
- github.com/go-logr/logr v1.4.1 // indirect
- github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
- github.com/google/go-cmp v0.6.0 // indirect
- github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
- golang.org/x/net v0.20.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- golang.org/x/tools v0.17.0 // indirect
-)
-
-require (
+ github.com/Microsoft/go-winio v0.6.2 // indirect
+ github.com/containerd/log v0.1.0 // indirect
+ github.com/distribution/reference v0.6.0 // indirect
+ github.com/docker/go-connections v0.4.0 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/go-openapi/errors v0.22.0 // indirect
+ github.com/go-openapi/strfmt v0.23.0 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/go-openapi/validate v0.24.0 // indirect
+ github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
- github.com/kr/text v0.2.0 // indirect
- github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
- github.com/onsi/ginkgo/v2 v2.16.0
- github.com/onsi/gomega v1.32.0
+ github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
+ github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe // indirect
+ github.com/moby/docker-image-spec v1.3.1 // indirect
+ github.com/opencontainers/go-digest v1.0.0 // indirect
+ github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
+ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/spf13/cobra v1.8.1 // indirect
+ github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
+ github.com/spf13/viper v1.19.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
- golang.org/x/sys v0.16.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
+ go.opentelemetry.io/otel v1.28.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
+ go.opentelemetry.io/otel/metric v1.28.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.28.0 // indirect
+ go.opentelemetry.io/otel/trace v1.28.0 // indirect
+ golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
+ golang.org/x/net v0.26.0 // indirect
+ golang.org/x/sync v0.7.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
+ golang.org/x/tools v0.22.0 // indirect
+ google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
+ gotest.tools/v3 v3.5.1 // indirect
+ k8s.io/apimachinery v0.30.2 // indirect
+ k8s.io/client-go v0.30.2 // indirect
+ k8s.io/klog/v2 v2.120.1 // indirect
+ k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
)
diff --git a/extras/hs-test/go.sum b/extras/hs-test/go.sum
index 479b0289814..fb555ad7abf 100644
--- a/extras/hs-test/go.sum
+++ b/extras/hs-test/go.sum
@@ -1,70 +1,268 @@
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cilium/checkmate v1.0.3 h1:CQC5eOmlAZeEjPrVZY3ZwEBH64lHlx9mXYdUehEwI5w=
+github.com/cilium/checkmate v1.0.3/go.mod h1:KiBTasf39/F2hf2yAmHw21YFl3hcEyP4Yk6filxc12A=
+github.com/cilium/cilium v1.15.7 h1:7LwGfAW/fR/VFcm6zlESjE2Ut5vJWe+kdWq3RNJrNRc=
+github.com/cilium/cilium v1.15.7/go.mod h1:6Ml8eeyWjMJKDeadutWhn5NibMps0H+yLOgfKBoHTUs=
+github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
+github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
+github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/edwarnicke/exechelper v1.0.3 h1:OY2ocGAITTqnEDvZk0dRQSeMIQvyH0SyL/4ncz+5GeQ=
github.com/edwarnicke/exechelper v1.0.3/go.mod h1:R65OUPKns4bgeHkCmfSHbmqLBU8aHZxTgLmEyUBUk4U=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
+github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
+github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
+github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
+github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
+github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
+github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
+github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
+github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
+github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
-github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
-github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
+github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
+github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
+github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
-github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
-github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
-github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
-github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
-github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe h1:ewr1srjRCmcQogPQ/NCx6XCk6LGVmsVCc9Y3vvPZj+Y=
+github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
+github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
+github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
+github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
+github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8=
+github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
+github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
+github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
+github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA=
+github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
+github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
+github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
+github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
+github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20240524165444-4d4ba1473f21 h1:tcHUxOT8j/R+0S+A1j8D2InqguXFNxAiij+8QFOlX7Y=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20240524165444-4d4ba1473f21/go.mod h1:whJevzBpTrid75eZy99s3DqCmy05NfibNaF2Ol5Ox5A=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
+github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.fd.io/govpp v0.10.0 h1:lL93SbqOILjON2pMvazrlHRekGYTRy0Qmj57RuAkxR0=
go.fd.io/govpp v0.10.0/go.mod h1:5m3bZM9ck+2EGC2O3ASmSSJAaoouyOlVWtiwj5BdCv0=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
+go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
+go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
+go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
+go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
+go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
+go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
+go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
+go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
+go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
+go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
+go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
+go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
+golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
-golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
+gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
+k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
+k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
+k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
+k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
+k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
diff --git a/extras/hs-test/hs_test.sh b/extras/hs-test/hs_test.sh
new file mode 100644
index 00000000000..85c0dd72705
--- /dev/null
+++ b/extras/hs-test/hs_test.sh
@@ -0,0 +1,147 @@
+#!/usr/bin/env bash
+
+source vars
+
+args=
+single_test=0
+persist_set=0
+unconfigure_set=0
+debug_set=0
+leak_check_set=0
+debug_build=
+ginkgo_args=
+tc_name=
+
+for i in "$@"
+do
+case "${i}" in
+ --persist=*)
+ persist="${i#*=}"
+ if [ "$persist" = "true" ]; then
+ args="$args -persist"
+ persist_set=1
+ fi
+ ;;
+ --debug=*)
+ debug="${i#*=}"
+ if [ "$debug" = "true" ]; then
+ args="$args -debug"
+ debug_set=1
+ fi
+ ;;
+ --debug_build=*)
+ debug_build="${i#*=}"
+ if [ "$debug_build" = "true" ]; then
+ args="$args -debug_build"
+ fi
+ ;;
+ --verbose=*)
+ verbose="${i#*=}"
+ if [ "$verbose" = "true" ]; then
+ args="$args -verbose"
+ fi
+ ;;
+ --unconfigure=*)
+ unconfigure="${i#*=}"
+ if [ "$unconfigure" = "true" ]; then
+ args="$args -unconfigure"
+ unconfigure_set=1
+ fi
+ ;;
+ --cpus=*)
+ args="$args -cpus ${i#*=}"
+ ;;
+ --vppsrc=*)
+ args="$args -vppsrc ${i#*=}"
+ ;;
+ --test=*)
+ tc_name="${i#*=}"
+ if [ "$tc_name" != "all" ]; then
+ single_test=1
+ ginkgo_args="$ginkgo_args --focus $tc_name -vv"
+ args="$args -verbose"
+ else
+ ginkgo_args="$ginkgo_args -v"
+ fi
+ ;;
+ --parallel=*)
+ ginkgo_args="$ginkgo_args -procs=${i#*=}"
+ ;;
+ --repeat=*)
+ ginkgo_args="$ginkgo_args --repeat=${i#*=}"
+ ;;
+ --cpu0=*)
+ cpu0="${i#*=}"
+ if [ "$cpu0" = "true" ]; then
+ args="$args -cpu0"
+ fi
+ ;;
+ --leak_check=*)
+ leak_check="${i#*=}"
+ if [ "$leak_check" = "true" ]; then
+ args="$args -leak_check"
+ leak_check_set=1
+ fi
+ ;;
+esac
+done
+
+if [ $single_test -eq 0 ] && [ $persist_set -eq 1 ]; then
+ echo "persist flag is not supported while running all tests!"
+ exit 1
+fi
+
+if [ $unconfigure_set -eq 1 ] && [ $single_test -eq 0 ]; then
+ echo "a single test has to be specified when unconfigure is set"
+ exit 1
+fi
+
+if [ $persist_set -eq 1 ] && [ $unconfigure_set -eq 1 ]; then
+ echo "setting persist flag and unconfigure flag is not allowed"
+ exit 1
+fi
+
+if [ $single_test -eq 0 ] && [ $debug_set -eq 1 ]; then
+ echo "VPP debug flag is not supported while running all tests!"
+ exit 1
+fi
+
+if [ $leak_check_set -eq 1 ]; then
+ if [ $single_test -eq 0 ]; then
+ echo "a single test has to be specified when leak_check is set"
+ exit 1
+ fi
+ ginkgo_args="--focus $tc_name"
+ sudo -E go run github.com/onsi/ginkgo/v2/ginkgo $ginkgo_args -- $args
+ exit 0
+fi
+
+if [ -n "${BUILD_NUMBER}" ]; then
+ ginkgo_args="$ginkgo_args --no-color"
+fi
+
+mkdir -p summary
+# shellcheck disable=SC2086
+sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --json-report=summary/report.json $ginkgo_args -- $args
+
+if [ $? != 0 ]; then
+ jq -r '.[0] | .SpecReports[] | select((.State == "failed") or (.State == "timedout") or (.State == "panicked")) | select(.Failure != null) |
+"TestName:
+ \(.LeafNodeText)
+Suite:
+ \(.Failure.FailureNodeLocation.FileName)
+Message:\n"
++(if .ReportEntries? then .ReportEntries[] | select(.Name == "VPP Backtrace") |
+"\tVPP crashed
+Full Back Trace:
+\(.Value.Representation | ltrimstr("{{red}}") | rtrimstr("{{/}}"))" else
+ "\(.Failure.Message)"
+ + (if .Failure.Message == "A spec timeout occurred" then "\n" else
+"\nFull Stack Trace:
+\(.Failure.Location.FullStackTrace)\n" end) end)' summary/report.json > summary/failed-summary.log \
+ && echo "Summary generated -> summary/failed-summary.log"
+else
+ if [ -e "summary/failed-summary.log" ]; then
+ rm summary/failed-summary.log
+ fi
+fi
diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go
deleted file mode 100644
index 725fee73f24..00000000000
--- a/extras/hs-test/hst_suite.go
+++ /dev/null
@@ -1,503 +0,0 @@
-package main
-
-import (
- "bufio"
- "errors"
- "flag"
- "fmt"
- "io"
- "log"
- "os"
- "os/exec"
- "strings"
- "time"
-
- "github.com/edwarnicke/exechelper"
- . "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
- "gopkg.in/yaml.v3"
-)
-
-const (
- DEFAULT_NETWORK_NUM int = 1
-)
-
-var isPersistent = flag.Bool("persist", false, "persists topology config")
-var isVerbose = flag.Bool("verbose", false, "verbose test output")
-var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
-var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
-var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp")
-var vppSourceFileDir = flag.String("vppsrc", "", "vpp source file directory")
-
-type HstSuite struct {
- containers map[string]*Container
- volumes []string
- netConfigs []NetConfig
- netInterfaces map[string]*NetInterface
- ip4AddrAllocator *Ip4AddressAllocator
- testIds map[string]string
- cpuAllocator *CpuAllocatorT
- cpuContexts []*CpuContext
- cpuPerVpp int
- pid string
- logger *log.Logger
- logFile *os.File
-}
-
-func (s *HstSuite) SetupSuite() {
- s.createLogger()
- s.log("Suite Setup")
- RegisterFailHandler(func(message string, callerSkip ...int) {
- s.hstFail()
- Fail(message, callerSkip...)
- })
- var err error
- s.pid = fmt.Sprint(os.Getpid())
- s.cpuAllocator, err = CpuAllocator()
- if err != nil {
- Fail("failed to init cpu allocator: " + fmt.Sprint(err))
- }
- s.cpuPerVpp = *nConfiguredCpus
-}
-
-func (s *HstSuite) AllocateCpus() []int {
- cpuCtx, err := s.cpuAllocator.Allocate(s.cpuPerVpp)
- s.assertNil(err)
- s.AddCpuContext(cpuCtx)
- return cpuCtx.cpus
-}
-
-func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
- s.cpuContexts = append(s.cpuContexts, cpuCtx)
-}
-
-func (s *HstSuite) TearDownSuite() {
- defer s.logFile.Close()
- s.log("Suite Teardown")
- s.unconfigureNetworkTopology()
-}
-
-func (s *HstSuite) TearDownTest() {
- s.log("Test Teardown")
- if *isPersistent {
- return
- }
- for _, c := range s.cpuContexts {
- c.Release()
- }
- s.resetContainers()
- s.removeVolumes()
- s.ip4AddrAllocator.deleteIpAddresses()
-}
-
-func (s *HstSuite) skipIfUnconfiguring() {
- if *isUnconfiguring {
- s.skip("skipping to unconfigure")
- }
-}
-
-func (s *HstSuite) SetupTest() {
- s.log("Test Setup")
- s.skipIfUnconfiguring()
- s.setupVolumes()
- s.setupContainers()
-}
-
-func (s *HstSuite) setupVolumes() {
- for _, volume := range s.volumes {
- cmd := "docker volume create --name=" + volume
- s.log(cmd)
- exechelper.Run(cmd)
- }
-}
-
-func (s *HstSuite) setupContainers() {
- for _, container := range s.containers {
- if !container.isOptional {
- container.run()
- }
- }
-}
-
-func (s *HstSuite) logVppInstance(container *Container, maxLines int) {
- if container.vppInstance == nil {
- return
- }
-
- logSource := container.getHostWorkDir() + defaultLogFilePath
- file, err := os.Open(logSource)
-
- if err != nil {
- return
- }
- defer file.Close()
-
- scanner := bufio.NewScanner(file)
- var lines []string
- var counter int
-
- for scanner.Scan() {
- lines = append(lines, scanner.Text())
- counter++
- if counter > maxLines {
- lines = lines[1:]
- counter--
- }
- }
-
- s.log("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:")
- for _, line := range lines {
- s.log(line)
- }
- s.log("^^^^^^^^^^^^^^^\n\n")
-}
-
-func (s *HstSuite) hstFail() {
- s.log("Containers: " + fmt.Sprint(s.containers))
- for _, container := range s.containers {
- out, err := container.log(20)
- if err != nil {
- fmt.Printf("An error occured while obtaining '%s' container logs: %s\n", container.name, fmt.Sprint(err))
- continue
- }
- s.log("\nvvvvvvvvvvvvvvv " +
- container.name + ":\n" +
- out +
- "^^^^^^^^^^^^^^^\n\n")
- s.logVppInstance(container, 20)
- }
-}
-
-func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
- Expect(object).To(BeNil(), msgAndArgs...)
-}
-
-func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
- Expect(object).ToNot(BeNil(), msgAndArgs...)
-}
-
-func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
- Expect(actual).To(Equal(expected), msgAndArgs...)
-}
-
-func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
- Expect(actual).ToNot(Equal(expected), msgAndArgs...)
-}
-
-func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
- Expect(testString).To(ContainSubstring(fmt.Sprint(contains)), msgAndArgs...)
-}
-
-func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
- Expect(testString).ToNot(ContainSubstring(fmt.Sprint(contains)), msgAndArgs...)
-}
-
-func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
- Expect(object).ToNot(BeEmpty(), msgAndArgs...)
-}
-
-func (s *HstSuite) createLogger(){
- suiteName := CurrentSpecReport().ContainerHierarchyTexts[0]
- var err error
- s.logFile, err = os.Create("summary/" + suiteName + ".log")
- if err != nil {
- Fail("Unable to create log file.")
- }
- s.logger = log.New(io.Writer(s.logFile), "", log.LstdFlags)
-}
-
-// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter
-// to keep console tidy
-func (s *HstSuite) log(arg any) {
- logs := strings.Split(fmt.Sprint(arg), "\n")
- for _, line := range logs {
- s.logger.Println(line)
- }
- if *isVerbose {
- GinkgoWriter.Println(arg)
- }
-}
-
-func (s *HstSuite) skip(args string) {
- Skip(args)
-}
-
-func (s *HstSuite) SkipIfMultiWorker(args ...any) {
- if *nConfiguredCpus > 1 {
- s.skip("test case not supported with multiple vpp workers")
- }
-}
-
-func (s *HstSuite) SkipUnlessExtendedTestsBuilt() {
- imageName := "hs-test/nginx-http3"
-
- cmd := exec.Command("docker", "images", imageName)
- byteOutput, err := cmd.CombinedOutput()
- if err != nil {
- s.log("error while searching for docker image")
- return
- }
- if !strings.Contains(string(byteOutput), imageName) {
- s.skip("extended tests not built")
- }
-}
-
-func (s *HstSuite) resetContainers() {
- for _, container := range s.containers {
- container.stop()
- }
-}
-
-func (s *HstSuite) removeVolumes() {
- for _, volumeName := range s.volumes {
- cmd := "docker volume rm " + volumeName
- exechelper.Run(cmd)
- os.RemoveAll(volumeName)
- }
-}
-
-func (s *HstSuite) getNetNamespaceByName(name string) string {
- return name + s.pid
-}
-
-func (s *HstSuite) getInterfaceByName(name string) *NetInterface {
- return s.netInterfaces[name+s.pid]
-}
-
-func (s *HstSuite) getContainerByName(name string) *Container {
- return s.containers[name+s.pid]
-}
-
-/*
- * Create a copy and return its address, so that individial tests which call this
- * are not able to modify the original container and affect other tests by doing that
- */
-func (s *HstSuite) getTransientContainerByName(name string) *Container {
- containerCopy := *s.containers[name+s.pid]
- return &containerCopy
-}
-
-func (s *HstSuite) loadContainerTopology(topologyName string) {
- data, err := os.ReadFile(containerTopologyDir + topologyName + ".yaml")
- if err != nil {
- Fail("read error: " + fmt.Sprint(err))
- }
- var yamlTopo YamlTopology
- err = yaml.Unmarshal(data, &yamlTopo)
- if err != nil {
- Fail("unmarshal error: " + fmt.Sprint(err))
- }
-
- for _, elem := range yamlTopo.Volumes {
- volumeMap := elem["volume"].(VolumeConfig)
- hostDir := volumeMap["host-dir"].(string)
- workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + volumeDir
- volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir)
- hostDir = volDirReplacer.Replace(hostDir)
- s.volumes = append(s.volumes, hostDir)
- }
-
- s.containers = make(map[string]*Container)
- for _, elem := range yamlTopo.Containers {
- newContainer, err := newContainer(s, elem)
- newContainer.suite = s
- newContainer.name += newContainer.suite.pid
- if err != nil {
- Fail("container config error: " + fmt.Sprint(err))
- }
- s.containers[newContainer.name] = newContainer
- }
-}
-
-func (s *HstSuite) loadNetworkTopology(topologyName string) {
- data, err := os.ReadFile(networkTopologyDir + topologyName + ".yaml")
- if err != nil {
- Fail("read error: " + fmt.Sprint(err))
- }
- var yamlTopo YamlTopology
- err = yaml.Unmarshal(data, &yamlTopo)
- if err != nil {
- Fail("unmarshal error: " + fmt.Sprint(err))
- }
-
- s.ip4AddrAllocator = NewIp4AddressAllocator()
- s.netInterfaces = make(map[string]*NetInterface)
-
- for _, elem := range yamlTopo.Devices {
- if _, ok := elem["name"]; ok {
- elem["name"] = elem["name"].(string) + s.pid
- }
-
- if peer, ok := elem["peer"].(NetDevConfig); ok {
- if peer["name"].(string) != "" {
- peer["name"] = peer["name"].(string) + s.pid
- }
- if _, ok := peer["netns"]; ok {
- peer["netns"] = peer["netns"].(string) + s.pid
- }
- }
-
- if _, ok := elem["netns"]; ok {
- elem["netns"] = elem["netns"].(string) + s.pid
- }
-
- if _, ok := elem["interfaces"]; ok {
- interfaceCount := len(elem["interfaces"].([]interface{}))
- for i := 0; i < interfaceCount; i++ {
- elem["interfaces"].([]interface{})[i] = elem["interfaces"].([]interface{})[i].(string) + s.pid
- }
- }
-
- switch elem["type"].(string) {
- case NetNs:
- {
- if namespace, err := newNetNamespace(elem); err == nil {
- s.netConfigs = append(s.netConfigs, &namespace)
- } else {
- Fail("network config error: " + fmt.Sprint(err))
- }
- }
- case Veth, Tap:
- {
- if netIf, err := newNetworkInterface(elem, s.ip4AddrAllocator); err == nil {
- s.netConfigs = append(s.netConfigs, netIf)
- s.netInterfaces[netIf.Name()] = netIf
- } else {
- Fail("network config error: " + fmt.Sprint(err))
- }
- }
- case Bridge:
- {
- if bridge, err := newBridge(elem); err == nil {
- s.netConfigs = append(s.netConfigs, &bridge)
- } else {
- Fail("network config error: " + fmt.Sprint(err))
- }
- }
- }
- }
-}
-
-func (s *HstSuite) configureNetworkTopology(topologyName string) {
- s.loadNetworkTopology(topologyName)
-
- if *isUnconfiguring {
- return
- }
-
- for _, nc := range s.netConfigs {
- if err := nc.configure(); err != nil {
- Fail("Network config error: " + fmt.Sprint(err))
- }
- }
-}
-
-func (s *HstSuite) unconfigureNetworkTopology() {
- if *isPersistent {
- return
- }
- for _, nc := range s.netConfigs {
- nc.unconfigure()
- }
-}
-
-func (s *HstSuite) getTestId() string {
- testName := CurrentSpecReport().LeafNodeText
-
- if s.testIds == nil {
- s.testIds = map[string]string{}
- }
-
- if _, ok := s.testIds[testName]; !ok {
- s.testIds[testName] = time.Now().Format("2006-01-02_15-04-05")
- }
-
- return s.testIds[testName]
-}
-
-// Returns last 4 digits of PID
-func (s *HstSuite) getPortFromPid() string {
- port := s.pid
- for len(port) < 4 {
- port += "0"
- }
- return port[len(port)-4:]
-}
-
-func (s *HstSuite) startServerApp(running chan error, done chan struct{}, env []string) {
- cmd := exec.Command("iperf3", "-4", "-s", "-p", s.getPortFromPid())
- if env != nil {
- cmd.Env = env
- }
- s.log(cmd)
- err := cmd.Start()
- if err != nil {
- msg := fmt.Errorf("failed to start iperf server: %v", err)
- running <- msg
- return
- }
- running <- nil
- <-done
- cmd.Process.Kill()
-}
-
-func (s *HstSuite) startClientApp(ipAddress string, env []string, clnCh chan error, clnRes chan string) {
- defer func() {
- clnCh <- nil
- }()
-
- nTries := 0
-
- for {
- cmd := exec.Command("iperf3", "-c", ipAddress, "-u", "-l", "1460", "-b", "10g", "-p", s.getPortFromPid())
- if env != nil {
- cmd.Env = env
- }
- s.log(cmd)
- o, err := cmd.CombinedOutput()
- if err != nil {
- if nTries > 5 {
- clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o)
- return
- }
- time.Sleep(1 * time.Second)
- nTries++
- continue
- } else {
- clnRes <- fmt.Sprintf("Client output: %s", o)
- }
- break
- }
-}
-
-func (s *HstSuite) startHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) {
- cmd := newCommand([]string{"./http_server", addressPort, s.pid}, netNs)
- err := cmd.Start()
- s.log(cmd)
- if err != nil {
- fmt.Println("Failed to start http server: " + fmt.Sprint(err))
- return
- }
- running <- struct{}{}
- <-done
- cmd.Process.Kill()
-}
-
-func (s *HstSuite) startWget(finished chan error, server_ip, port, query, netNs string) {
- defer func() {
- finished <- errors.New("wget error")
- }()
-
- cmd := newCommand([]string{"wget", "--timeout=10", "--no-proxy", "--tries=5", "-O", "/dev/null", server_ip + ":" + port + "/" + query},
- netNs)
- s.log(cmd)
- o, err := cmd.CombinedOutput()
- if err != nil {
- finished <- fmt.Errorf("wget error: '%v\n\n%s'", err, o)
- return
- } else if !strings.Contains(string(o), "200 OK") {
- finished <- fmt.Errorf("wget error: response not 200 OK")
- return
- }
- finished <- nil
-}
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
index fe12f5a2f65..bfd2d34483c 100644
--- a/extras/hs-test/http_test.go
+++ b/extras/hs-test/http_test.go
@@ -1,193 +1,1156 @@
package main
import (
+ "bytes"
"fmt"
+ "github.com/onsi/gomega/ghttp"
+ "github.com/onsi/gomega/gmeasure"
+ "io"
+ "math/rand"
+ "net"
+ "net/http"
+ "net/http/httptrace"
"os"
- "strings"
+ "strconv"
+ "sync"
"time"
+ . "fd.io/hs-test/infra"
. "github.com/onsi/ginkgo/v2"
)
func init() {
- registerNsTests(HttpTpsTest)
- registerVethTests(HttpCliTest)
- registerNoTopoTests(NginxHttp3Test, NginxAsServerTest,
- NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest, HeaderServerTest)
- registerNoTopoSoloTests(HttpStaticPromTest)
+ RegisterVethTests(HttpCliTest, HttpCliConnectErrorTest)
+ RegisterSoloVethTests(HttpClientGetMemLeakTest)
+ RegisterNoTopoTests(HeaderServerTest, HttpPersistentConnectionTest, HttpPipeliningTest,
+ HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest,
+ HttpCliBadRequestTest, HttpStaticBuildInUrlGetIfStatsTest, HttpStaticBuildInUrlPostIfStatsTest,
+ HttpInvalidRequestLineTest, HttpMethodNotImplementedTest, HttpInvalidHeadersTest,
+ HttpContentLengthTest, HttpStaticBuildInUrlGetIfListTest, HttpStaticBuildInUrlGetVersionTest,
+ HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest,
+ HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathTraversalTest, HttpUriDecodeTest,
+ HttpHeadersTest, HttpStaticFileHandlerTest, HttpStaticFileHandlerDefaultMaxAgeTest, HttpClientTest, HttpClientErrRespTest, HttpClientPostFormTest,
+ HttpClientPostFileTest, HttpClientPostFilePtrTest, AuthorityFormTargetTest, HttpRequestLineTest)
+ RegisterNoTopoSoloTests(HttpStaticPromTest, HttpGetTpsTest, HttpGetTpsInterruptModeTest, PromConcurrentConnectionsTest,
+ PromMemLeakTest, HttpClientPostMemLeakTest, HttpInvalidClientRequestMemLeakTest, HttpPostTpsTest, HttpPostTpsInterruptModeTest)
}
-func HttpTpsTest(s *NsSuite) {
- iface := s.getInterfaceByName(clientInterface)
- client_ip := iface.ip4AddressString()
- port := "8080"
- finished := make(chan error, 1)
- clientNetns := s.getNetNamespaceByName("cln")
+const wwwRootPath = "/tmp/www_root"
- container := s.getContainerByName("vpp")
+func httpDownloadBenchmark(s *HstSuite, experiment *gmeasure.Experiment, data interface{}) {
+ url, isValid := data.(string)
+ s.AssertEqual(true, isValid)
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", url, nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ t := time.Now()
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.AssertHttpStatus(resp, 200)
+ _, err = io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ duration := time.Since(t)
+ experiment.RecordValue("Download Speed", (float64(resp.ContentLength)/1024/1024)/duration.Seconds(), gmeasure.Units("MB/s"), gmeasure.Precision(2))
+}
+
+func HttpGetTpsInterruptModeTest(s *NoTopoSuite) {
+ HttpGetTpsTest(s)
+}
+
+func HttpGetTpsTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ url := "http://" + serverAddress + ":8080/test_file_10M"
+
+ vpp.Vppctl("http tps uri tcp://0.0.0.0/8080")
+
+ s.RunBenchmark("HTTP tps download 10M", 10, 0, httpDownloadBenchmark, url)
+}
- // configure vpp in the container
- container.vppInstance.vppctl("http tps uri tcp://0.0.0.0/8080")
+func httpUploadBenchmark(s *HstSuite, experiment *gmeasure.Experiment, data interface{}) {
+ url, isValid := data.(string)
+ s.AssertEqual(true, isValid)
+ body := make([]byte, 10485760)
+ _, err := rand.Read(body)
+ client := NewHttpClient()
+ req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
+ s.AssertNil(err, fmt.Sprint(err))
+ t := time.Now()
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.AssertHttpStatus(resp, 200)
+ _, err = io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ duration := time.Since(t)
+ experiment.RecordValue("Upload Speed", (float64(req.ContentLength)/1024/1024)/duration.Seconds(), gmeasure.Units("MB/s"), gmeasure.Precision(2))
+}
- go func() {
- defer GinkgoRecover()
- s.startWget(finished, client_ip, port, "test_file_10M", clientNetns)
- }()
- // wait for client
- err := <-finished
- s.assertNil(err, fmt.Sprint(err))
+func HttpPostTpsInterruptModeTest(s *NoTopoSuite) {
+ HttpPostTpsTest(s)
+}
+
+func HttpPostTpsTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ url := "http://" + serverAddress + ":8080/test_file_10M"
+
+ vpp.Vppctl("http tps uri tcp://0.0.0.0/8080")
+
+ s.RunBenchmark("HTTP tps upload 10M", 10, 0, httpUploadBenchmark, url)
+}
+
+func HttpPersistentConnectionTest(s *NoTopoSuite) {
+ // testing url handler app do not support multi-thread
+ s.SkipIfMultiWorker()
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers"))
+ s.Log(vpp.Vppctl("test-url-handler enable"))
+
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ transport.(*http.Transport).DisableKeepAlives = false
+ client := &http.Client{
+ Transport: transport,
+ Timeout: time.Second * 30,
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ }}
+
+ body := []byte("{\"sandwich\": {\"spam\": 2, \"eggs\": 1}}")
+ req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test3", bytes.NewBuffer(body))
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertEqual(false, resp.Close)
+ s.AssertHttpContentLength(resp, int64(0))
+ o1 := vpp.Vppctl("show session verbose proto http state ready")
+ s.Log(o1)
+ s.AssertContains(o1, "ESTABLISHED")
+
+ req, err = http.NewRequest("GET", "http://"+serverAddress+":80/test1", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ clientTrace := &httptrace.ClientTrace{
+ GotConn: func(info httptrace.GotConnInfo) {
+ s.AssertEqual(true, info.Reused, "connection not reused")
+ },
+ }
+ req = req.WithContext(httptrace.WithClientTrace(req.Context(), clientTrace))
+ resp, err = client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertEqual(false, resp.Close)
+ s.AssertHttpBody(resp, "hello")
+ o2 := vpp.Vppctl("show session verbose proto http state ready")
+ s.Log(o2)
+ s.AssertContains(o2, "ESTABLISHED")
+ s.AssertEqual(o1, o2)
+
+ req, err = http.NewRequest("GET", "http://"+serverAddress+":80/test2", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ req = req.WithContext(httptrace.WithClientTrace(req.Context(), clientTrace))
+ resp, err = client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertEqual(false, resp.Close)
+ s.AssertHttpBody(resp, "some data")
+ o2 = vpp.Vppctl("show session verbose proto http state ready")
+ s.Log(o2)
+ s.AssertContains(o2, "ESTABLISHED")
+ s.AssertEqual(o1, o2)
+
+}
+
+func HttpPipeliningTest(s *NoTopoSuite) {
+ // testing url handler app do not support multi-thread
+ s.SkipIfMultiWorker()
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+ s.Log(vpp.Vppctl("test-url-handler enable"))
+
+ req1 := "GET /test_delayed HTTP/1.1\r\nHost:" + serverAddress + ":80\r\nUser-Agent:test\r\n\r\n"
+ req2 := "GET /test1 HTTP/1.1\r\nHost:" + serverAddress + ":80\r\nUser-Agent:test\r\n\r\n"
+
+ conn, err := net.DialTimeout("tcp", serverAddress+":80", time.Second*30)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer conn.Close()
+ err = conn.SetDeadline(time.Now().Add(time.Second * 15))
+ s.AssertNil(err, fmt.Sprint(err))
+ n, err := conn.Write([]byte(req1))
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertEqual(n, len([]rune(req1)))
+ // send second request a bit later so first is already in progress
+ time.Sleep(500 * time.Millisecond)
+ n, err = conn.Write([]byte(req2))
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertEqual(n, len([]rune(req2)))
+ reply := make([]byte, 1024)
+ n, err = conn.Read(reply)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log(string(reply))
+ s.AssertContains(string(reply), "delayed data", "first request response not received")
+ s.AssertNotContains(string(reply), "hello", "second request response received")
+ // make sure response for second request is not received later
+ _, err = conn.Read(reply)
+ s.AssertMatchError(err, os.ErrDeadlineExceeded, "second request response received")
}
func HttpCliTest(s *VethsSuite) {
- serverContainer := s.getContainerByName("server-vpp")
- clientContainer := s.getContainerByName("client-vpp")
+ serverContainer := s.GetContainerByName("server-vpp")
+ clientContainer := s.GetContainerByName("client-vpp")
- serverVeth := s.getInterfaceByName(serverInterfaceName)
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
- serverContainer.vppInstance.vppctl("http cli server")
+ serverContainer.VppInstance.Vppctl("http cli server")
- uri := "http://" + serverVeth.ip4AddressString() + "/80"
+ uri := "http://" + serverVeth.Ip4AddressString() + "/80"
- o := clientContainer.vppInstance.vppctl("http cli client" +
+ o := clientContainer.VppInstance.Vppctl("http cli client" +
" uri " + uri + " query /show/vlib/graph")
- s.log(o)
- s.assertContains(o, "<html>", "<html> not found in the result!")
+ s.Log(o)
+ s.AssertContains(o, "<html>", "<html> not found in the result!")
+ s.AssertContains(o, "</html>", "</html> not found in the result!")
}
-func NginxHttp3Test(s *NoTopoSuite) {
- s.SkipUnlessExtendedTestsBuilt()
+func HttpCliConnectErrorTest(s *VethsSuite) {
+ clientContainer := s.GetContainerByName("client-vpp")
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
- query := "index.html"
- nginxCont := s.getContainerByName("nginx-http3")
- s.assertNil(nginxCont.run())
+ uri := "http://" + serverVeth.Ip4AddressString() + "/80"
- vpp := s.getContainerByName("vpp").vppInstance
- vpp.waitForApp("nginx-", 5)
- serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+ o := clientContainer.VppInstance.Vppctl("http cli client" +
+ " uri " + uri + " query /show/vlib/graph")
- defer func() { os.Remove(query) }()
- curlCont := s.getContainerByName("curl")
- args := fmt.Sprintf("curl --noproxy '*' --local-port 55444 --http3-only -k https://%s:8443/%s", serverAddress, query)
- curlCont.extraRunningArgs = args
- o, err := curlCont.combinedOutput()
- s.assertNil(err, fmt.Sprint(err))
- s.assertContains(o, "<http>", "<http> not found in the result!")
+ s.Log(o)
+ s.AssertContains(o, "failed to connect")
+}
+
+func HttpClientTest(s *NoTopoSuite) {
+ serverAddress := s.HostAddr()
+ server := ghttp.NewUnstartedServer()
+ l, err := net.Listen("tcp", serverAddress+":80")
+ s.AssertNil(err, fmt.Sprint(err))
+ server.HTTPTestServer.Listener = l
+ server.AppendHandlers(
+ ghttp.CombineHandlers(
+ s.LogHttpReq(true),
+ ghttp.VerifyRequest("GET", "/test"),
+ ghttp.VerifyHeader(http.Header{"User-Agent": []string{"http_cli_client"}}),
+ ghttp.VerifyHeader(http.Header{"Accept": []string{"text/html"}}),
+ ghttp.RespondWith(http.StatusOK, "<html><body><p>Hello</p></body></html>"),
+ ))
+ server.Start()
+ defer server.Close()
+ uri := "http://" + serverAddress + "/80"
+ vpp := s.GetContainerByName("vpp").VppInstance
+ o := vpp.Vppctl("http cli client uri " + uri + " query /test")
+
+ s.Log(o)
+ s.AssertContains(o, "<html>", "<html> not found in the result!")
+ s.AssertContains(o, "</html>", "</html> not found in the result!")
+}
+
+func HttpClientErrRespTest(s *NoTopoSuite) {
+ serverAddress := s.HostAddr()
+ server := ghttp.NewUnstartedServer()
+ l, err := net.Listen("tcp", serverAddress+":80")
+ s.AssertNil(err, fmt.Sprint(err))
+ server.HTTPTestServer.Listener = l
+ server.AppendHandlers(
+ ghttp.CombineHandlers(
+ s.LogHttpReq(true),
+ ghttp.VerifyRequest("GET", "/test"),
+ ghttp.RespondWith(http.StatusNotFound, "404: Not Found"),
+ ))
+ server.Start()
+ defer server.Close()
+ uri := "http://" + serverAddress + "/80"
+ vpp := s.GetContainerByName("vpp").VppInstance
+ o := vpp.Vppctl("http cli client uri " + uri + " query /test")
+
+ s.Log(o)
+ s.AssertContains(o, "404: Not Found", "error not found in the result!")
+}
+
+func HttpClientPostFormTest(s *NoTopoSuite) {
+ serverAddress := s.HostAddr()
+ body := "field1=value1&field2=value2"
+
+ server := ghttp.NewUnstartedServer()
+ l, err := net.Listen("tcp", serverAddress+":80")
+ s.AssertNil(err, fmt.Sprint(err))
+ server.HTTPTestServer.Listener = l
+ server.AppendHandlers(
+ ghttp.CombineHandlers(
+ s.LogHttpReq(true),
+ ghttp.VerifyRequest("POST", "/test"),
+ ghttp.VerifyContentType("application/x-www-form-urlencoded"),
+ ghttp.VerifyBody([]byte(body)),
+ ghttp.RespondWith(http.StatusOK, nil),
+ ))
+ server.Start()
+ defer server.Close()
+
+ uri := "http://" + serverAddress + "/80"
+ vpp := s.GetContainerByName("vpp").VppInstance
+ o := vpp.Vppctl("http post uri " + uri + " target /test data " + body)
+
+ s.Log(o)
+ s.AssertNotContains(o, "error")
+}
+
+func httpClientPostFile(s *NoTopoSuite, usePtr bool, fileSize int) {
+ serverAddress := s.HostAddr()
+ vpp := s.GetContainerByName("vpp").VppInstance
+ fileName := "/tmp/test_file.txt"
+ s.Log(vpp.Container.Exec("fallocate -l " + strconv.Itoa(fileSize) + " " + fileName))
+ s.Log(vpp.Container.Exec("ls -la " + fileName))
+
+ server := ghttp.NewUnstartedServer()
+ l, err := net.Listen("tcp", serverAddress+":80")
+ s.AssertNil(err, fmt.Sprint(err))
+ server.HTTPTestServer.Listener = l
+ server.AppendHandlers(
+ ghttp.CombineHandlers(
+ s.LogHttpReq(false),
+ ghttp.VerifyRequest("POST", "/test"),
+ ghttp.VerifyHeader(http.Header{"Content-Length": []string{strconv.Itoa(fileSize)}}),
+ ghttp.VerifyContentType("application/octet-stream"),
+ ghttp.RespondWith(http.StatusOK, nil),
+ ))
+ server.Start()
+ defer server.Close()
+
+ uri := "http://" + serverAddress + "/80"
+ cmd := "http post uri " + uri + " target /test file " + fileName
+ if usePtr {
+ cmd += " use-ptr"
+ }
+ o := vpp.Vppctl(cmd)
+
+ s.Log(o)
+ s.AssertNotContains(o, "error")
+}
+
+func HttpClientPostFileTest(s *NoTopoSuite) {
+ httpClientPostFile(s, false, 32768)
+}
+
+func HttpClientPostFilePtrTest(s *NoTopoSuite) {
+ httpClientPostFile(s, true, 131072)
+}
+
+func cliTestAuthority(s *NoTopoSuite, authority string) {
+ o := s.GetContainerByName("vpp").VppInstance.Vppctl("test http authority-form " + authority)
+ s.AssertNotContains(o, "error")
+ s.AssertContains(o, authority)
+}
+
+func cliTestAuthorityError(s *NoTopoSuite, authority string) {
+ o := s.GetContainerByName("vpp").VppInstance.Vppctl("test http authority-form " + authority)
+ s.AssertContains(o, "error")
+}
+
+func AuthorityFormTargetTest(s *NoTopoSuite) {
+ cliTestAuthority(s, "10.10.2.45:20")
+ cliTestAuthority(s, "[dead:beef::1234]:443")
+ cliTestAuthorityError(s, "example.com:80")
+ cliTestAuthorityError(s, "10.10.2.45")
+ cliTestAuthorityError(s, "1000.10.2.45:20")
+ cliTestAuthorityError(s, "[xyz0::1234]:443")
}
func HttpStaticPromTest(s *NoTopoSuite) {
- finished := make(chan error, 1)
query := "stats.prom"
- vpp := s.getContainerByName("vpp").vppInstance
- serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
- s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers"))
- s.log(vpp.vppctl("prom enable"))
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers"))
+ s.Log(vpp.Vppctl("prom enable"))
time.Sleep(time.Second * 5)
- go func() {
- defer GinkgoRecover()
- s.startWget(finished, serverAddress, "80", query, "")
- }()
- err := <-finished
- s.assertNil(err)
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/"+query, nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, false))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "text/plain")
+ s.AssertGreaterThan(resp.ContentLength, 0)
+ _, err = io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
}
-func HeaderServerTest(s *NoTopoSuite) {
- query := "show/version"
- vpp := s.getContainerByName("vpp").vppInstance
- serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
- vpp.vppctl("http cli server")
-
- curlCont := s.getContainerByName("curl")
- args := fmt.Sprintf("curl -i -s http://%s:80/%s", serverAddress, query)
- curlCont.extraRunningArgs = args
- o, err := curlCont.combinedOutput()
- s.assertNil(err, fmt.Sprint(err))
- s.log(o)
- s.assertContains(o, "Server: http_cli_server")
-}
-
-func NginxAsServerTest(s *NoTopoSuite) {
- query := "return_ok"
- finished := make(chan error, 1)
-
- nginxCont := s.getContainerByName("nginx")
- s.assertNil(nginxCont.run())
-
- vpp := s.getContainerByName("vpp").vppInstance
- vpp.waitForApp("nginx-", 5)
-
- serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
-
- defer func() { os.Remove(query) }()
- go func() {
- defer GinkgoRecover()
- s.startWget(finished, serverAddress, "80", query, "")
- }()
- s.assertNil(<-finished)
-}
-
-func parseString(s, pattern string) string {
- temp := strings.Split(s, "\n")
- for _, item := range temp {
- if strings.Contains(item, pattern) {
- return item
- }
+func promReq(s *NoTopoSuite, url string) {
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", url, nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.AssertHttpStatus(resp, 200)
+ _, err = io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+}
+
+func promReqWg(s *NoTopoSuite, url string, wg *sync.WaitGroup) {
+ defer GinkgoRecover()
+ defer wg.Done()
+ promReq(s, url)
+}
+
+func PromConcurrentConnectionsTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ url := "http://" + serverAddress + ":80/stats.prom"
+
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers"))
+ s.Log(vpp.Vppctl("prom enable"))
+ time.Sleep(time.Second * 5)
+
+ var wg sync.WaitGroup
+ for i := 0; i < 20; i++ {
+ wg.Add(1)
+ go promReqWg(s, url, &wg)
}
- return ""
-}
-
-func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error {
- nRequests := 1000000
- nClients := 1000
-
- serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
-
- vpp := s.getContainerByName("vpp").vppInstance
-
- nginxCont := s.getContainerByName(singleTopoContainerNginx)
- s.assertNil(nginxCont.run())
- vpp.waitForApp("nginx-", 5)
-
- if ab_or_wrk == "ab" {
- abCont := s.getContainerByName("ab")
- args := fmt.Sprintf("-n %d -c %d", nRequests, nClients)
- if mode == "rps" {
- args += " -k"
- } else if mode != "cps" {
- return fmt.Errorf("invalid mode %s; expected cps/rps", mode)
- }
- // don't exit on socket receive errors
- args += " -r"
- args += " http://" + serverAddress + ":80/64B.json"
- abCont.extraRunningArgs = args
- time.Sleep(time.Second * 10)
- o, err := abCont.combinedOutput()
- rps := parseString(o, "Requests per second:")
- s.log(rps)
- s.log(err)
- s.assertNil(err, "err: '%s', output: '%s'", err, o)
+ wg.Wait()
+ s.Log(vpp.Vppctl("show session verbose proto http"))
+}
+
+func PromMemLeakTest(s *NoTopoSuite) {
+ s.SkipUnlessLeakCheck()
+
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ url := "http://" + serverAddress + ":80/stats.prom"
+
+ /* no goVPP less noise */
+ vpp.Disconnect()
+
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers"))
+ s.Log(vpp.Vppctl("prom enable"))
+ time.Sleep(time.Second * 3)
+
+ /* warmup request (FIB) */
+ promReq(s, url)
+
+ vpp.EnableMemoryTrace()
+ traces1, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+
+ /* collect stats couple of times */
+ for i := 0; i < 5; i++ {
+ time.Sleep(time.Second * 1)
+ promReq(s, url)
+ }
+
+ /* let's give it some time to clean up sessions */
+ time.Sleep(time.Second * 5)
+
+ traces2, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.MemLeakCheck(traces1, traces2)
+}
+
+func HttpClientGetMemLeakTest(s *VethsSuite) {
+ s.SkipUnlessLeakCheck()
+
+ serverContainer := s.GetContainerByName("server-vpp").VppInstance
+ clientContainer := s.GetContainerByName("client-vpp").VppInstance
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+
+ /* no goVPP less noise */
+ clientContainer.Disconnect()
+
+ serverContainer.Vppctl("http cli server")
+
+ uri := "http://" + serverVeth.Ip4AddressString() + "/80"
+
+ /* warmup request (FIB) */
+ clientContainer.Vppctl("http cli client uri " + uri + " query /show/version")
+
+ /* let's give it some time to clean up sessions, so local port can be reused and we have less noise */
+ time.Sleep(time.Second * 12)
+
+ clientContainer.EnableMemoryTrace()
+ traces1, err := clientContainer.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+
+ clientContainer.Vppctl("http cli client uri " + uri + " query /show/vlib/graph")
+
+ /* let's give it some time to clean up sessions */
+ time.Sleep(time.Second * 12)
+
+ traces2, err := clientContainer.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+ clientContainer.MemLeakCheck(traces1, traces2)
+}
+
+func HttpClientPostMemLeakTest(s *NoTopoSuite) {
+ s.SkipUnlessLeakCheck()
+
+ serverAddress := s.HostAddr()
+ body := "field1=value1&field2=value2"
+
+ uri := "http://" + serverAddress + "/80"
+ vpp := s.GetContainerByName("vpp").VppInstance
+
+ /* no goVPP less noise */
+ vpp.Disconnect()
+
+ server := ghttp.NewUnstartedServer()
+ l, err := net.Listen("tcp", serverAddress+":80")
+ s.AssertNil(err, fmt.Sprint(err))
+ server.HTTPTestServer.Listener = l
+ server.AppendHandlers(
+ ghttp.CombineHandlers(
+ ghttp.VerifyRequest("POST", "/test"),
+ ghttp.RespondWith(http.StatusOK, nil),
+ ),
+ ghttp.CombineHandlers(
+ ghttp.VerifyRequest("POST", "/test"),
+ ghttp.RespondWith(http.StatusOK, nil),
+ ),
+ )
+ server.Start()
+ defer server.Close()
+
+ /* warmup request (FIB) */
+ vpp.Vppctl("http post uri " + uri + " target /test data " + body)
+
+ /* let's give it some time to clean up sessions, so local port can be reused and we have less noise */
+ time.Sleep(time.Second * 12)
+
+ vpp.EnableMemoryTrace()
+ traces1, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+
+ vpp.Vppctl("http post uri " + uri + " target /test data " + body)
+
+ /* let's give it some time to clean up sessions */
+ time.Sleep(time.Second * 12)
+
+ traces2, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.MemLeakCheck(traces1, traces2)
+}
+
+func HttpInvalidClientRequestMemLeakTest(s *NoTopoSuite) {
+ s.SkipUnlessLeakCheck()
+
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+
+ /* no goVPP less noise */
+ vpp.Disconnect()
+
+ vpp.Vppctl("http cli server")
+
+ /* warmup request (FIB) */
+ _, err := TcpSendReceive(serverAddress+":80", "GET / HTTP/1.1\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+
+ /* let's give it some time to clean up sessions, so local port can be reused and we have less noise */
+ time.Sleep(time.Second * 12)
+
+ vpp.EnableMemoryTrace()
+ traces1, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+
+ _, err = TcpSendReceive(serverAddress+":80", "GET / HTTP/1.1\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+
+ /* let's give it some time to clean up sessions */
+ time.Sleep(time.Second * 12)
+
+ traces2, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.MemLeakCheck(traces1, traces2)
+
+}
+
+func HttpStaticFileHandlerDefaultMaxAgeTest(s *NoTopoSuite) {
+ HttpStaticFileHandlerTestFunction(s, "default")
+}
+
+func HttpStaticFileHandlerTest(s *NoTopoSuite) {
+ HttpStaticFileHandlerTestFunction(s, "123")
+}
+
+func HttpStaticFileHandlerTestFunction(s *NoTopoSuite, max_age string) {
+ var maxAgeFormatted string
+ if max_age == "default" {
+ maxAgeFormatted = ""
+ max_age = "600"
} else {
- wrkCont := s.getContainerByName("wrk")
- args := fmt.Sprintf("-c %d -t 2 -d 30 http://%s:80/64B.json", nClients,
- serverAddress)
- wrkCont.extraRunningArgs = args
- o, err := wrkCont.combinedOutput()
- rps := parseString(o, "requests")
- s.log(rps)
- s.log(err)
- s.assertNil(err, "err: '%s', output: '%s'", err, o)
+ maxAgeFormatted = "max-age " + max_age
}
- return nil
+
+ content := "<html><body><p>Hello</p></body></html>"
+ content2 := "<html><body><p>Page</p></body></html>"
+
+ vpp := s.GetContainerByName("vpp").VppInstance
+ vpp.Container.Exec("mkdir -p " + wwwRootPath)
+ err := vpp.Container.CreateFile(wwwRootPath+"/index.html", content)
+ s.AssertNil(err, fmt.Sprint(err))
+ err = vpp.Container.CreateFile(wwwRootPath+"/page.html", content2)
+ s.AssertNil(err, fmt.Sprint(err))
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug cache-size 2m " + maxAgeFormatted))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/index.html", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "text/html")
+ s.AssertHttpHeaderWithValue(resp, "Cache-Control", "max-age="+max_age)
+ parsedTime, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertTimeEqualWithinThreshold(parsedTime, time.Now(), time.Minute*5)
+ s.AssertEqual(len(resp.Header.Get("Last-Modified")), 29)
+ s.AssertHttpContentLength(resp, int64(len([]rune(content))))
+ s.AssertHttpBody(resp, content)
+ o := vpp.Vppctl("show http static server cache verbose")
+ s.Log(o)
+ s.AssertContains(o, "index.html")
+ s.AssertNotContains(o, "page.html")
+
+ resp, err = client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "text/html")
+ s.AssertHttpHeaderWithValue(resp, "Cache-Control", "max-age="+max_age)
+ s.AssertHttpContentLength(resp, int64(len([]rune(content))))
+ s.AssertHttpBody(resp, content)
+
+ req, err = http.NewRequest("GET", "http://"+serverAddress+":80/page.html", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err = client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "text/html")
+ s.AssertHttpHeaderWithValue(resp, "Cache-Control", "max-age="+max_age)
+ s.AssertHttpContentLength(resp, int64(len([]rune(content2))))
+ s.AssertHttpBody(resp, content2)
+ o = vpp.Vppctl("show http static server cache verbose")
+ s.Log(o)
+ s.AssertContains(o, "index.html")
+ s.AssertContains(o, "page.html")
}
-func NginxPerfCpsTest(s *NoTopoSuite) {
- s.assertNil(runNginxPerf(s, "cps", "ab"))
+func HttpStaticPathTraversalTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ vpp.Container.Exec("mkdir -p " + wwwRootPath)
+ vpp.Container.Exec("mkdir -p " + "/tmp/secret_folder")
+ err := vpp.Container.CreateFile("/tmp/secret_folder/secret_file.txt", "secret")
+ s.AssertNil(err, fmt.Sprint(err))
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/../secret_folder/secret_file.txt", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 404)
+ s.AssertHttpHeaderNotPresent(resp, "Content-Type")
+ s.AssertHttpHeaderNotPresent(resp, "Cache-Control")
+ s.AssertHttpContentLength(resp, int64(0))
}
-func NginxPerfRpsTest(s *NoTopoSuite) {
- s.assertNil(runNginxPerf(s, "rps", "ab"))
+func HttpStaticMovedTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ vpp.Container.Exec("mkdir -p " + wwwRootPath + "/tmp.aaa")
+ err := vpp.Container.CreateFile(wwwRootPath+"/tmp.aaa/index.html", "<html><body><p>Hello</p></body></html>")
+ s.AssertNil(err, fmt.Sprint(err))
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/tmp.aaa", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 301)
+ s.AssertHttpHeaderWithValue(resp, "Location", "http://"+serverAddress+"/tmp.aaa/index.html")
+ s.AssertHttpHeaderNotPresent(resp, "Content-Type")
+ s.AssertHttpHeaderNotPresent(resp, "Cache-Control")
+ s.AssertHttpContentLength(resp, int64(0))
}
-func NginxPerfWrkTest(s *NoTopoSuite) {
- s.assertNil(runNginxPerf(s, "", "wrk"))
+func HttpStaticNotFoundTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ vpp.Container.Exec("mkdir -p " + wwwRootPath)
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/notfound.html", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 404)
+ s.AssertHttpHeaderNotPresent(resp, "Content-Type")
+ s.AssertHttpHeaderNotPresent(resp, "Cache-Control")
+ s.AssertHttpContentLength(resp, int64(0))
+}
+
+func HttpCliMethodNotAllowedTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 405)
+ s.AssertHttpHeaderWithValue(resp, "Allow", "GET", "server MUST generate an Allow header")
+ s.AssertHttpHeaderNotPresent(resp, "Content-Type")
+ s.AssertHttpContentLength(resp, int64(0))
+}
+
+func HttpCliBadRequestTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 400)
+ s.AssertHttpHeaderNotPresent(resp, "Content-Type")
+ s.AssertHttpContentLength(resp, int64(0))
+}
+
+func HttpStaticBuildInUrlGetVersionTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/version.json", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(string(data), "vpp_details")
+ s.AssertContains(string(data), "version")
+ s.AssertContains(string(data), "build_date")
+ s.AssertNotContains(string(data), "build_by")
+ s.AssertNotContains(string(data), "build_host")
+ s.AssertNotContains(string(data), "build_dir")
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "application/json")
+}
+
+func HttpStaticBuildInUrlGetVersionVerboseTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/version.json?verbose=true", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(string(data), "vpp_details")
+ s.AssertContains(string(data), "version")
+ s.AssertContains(string(data), "build_date")
+ s.AssertContains(string(data), "build_by")
+ s.AssertContains(string(data), "build_host")
+ s.AssertContains(string(data), "build_dir")
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "application/json")
+}
+
+func HttpStaticBuildInUrlGetIfListTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/interface_list.json", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(string(data), "interface_list")
+ s.AssertContains(string(data), s.VppIfName())
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "application/json")
+}
+
+func HttpStaticBuildInUrlGetIfStatsTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/interface_stats.json", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(string(data), "interface_stats")
+ s.AssertContains(string(data), "local0")
+ s.AssertContains(string(data), s.VppIfName())
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "application/json")
+}
+
+func validatePostInterfaceStats(s *NoTopoSuite, data string) {
+ s.AssertContains(data, "interface_stats")
+ s.AssertContains(data, s.VppIfName())
+ s.AssertNotContains(data, "error")
+ s.AssertNotContains(data, "local0")
+}
+
+func HttpStaticBuildInUrlPostIfStatsTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+ body := []byte(s.VppIfName())
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("POST",
+ "http://"+serverAddress+":80/interface_stats.json", bytes.NewBuffer(body))
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ validatePostInterfaceStats(s, string(data))
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "application/json")
+}
+
+func HttpStaticMacTimeTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+ s.Log(vpp.Vppctl("mactime enable-disable " + s.VppIfName()))
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/mactime.json", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(string(data), "mactime")
+ s.AssertContains(string(data), s.HostAddr())
+ s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).HwAddress.String())
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "application/json")
+ parsedTime, err := time.Parse(time.RFC1123, resp.Header.Get("Date"))
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertTimeEqualWithinThreshold(parsedTime, time.Now(), time.Minute*5)
+ s.AssertEqual(len(resp.Header.Get("Date")), 29)
+}
+
+func HttpInvalidRequestLineTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ resp, err := TcpSendReceive(serverAddress+":80", " GET / HTTP/1.1")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid request line start not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "\rGET / HTTP/1.1")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid request line start not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "\nGET / HTTP/1.1")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid request line start not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET / HTTP/1.1")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid framing not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET / HTTP/1.1\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid framing not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "HTTP-version must be present")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "request-target must be present")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "request-target must be present")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET / HTTP/x\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "'HTTP/x' invalid http version not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET / HTTP1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "'HTTP1.1' invalid http version not allowed")
+}
+
+func HttpRequestLineTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ resp, err := TcpSendReceive(serverAddress+":80", "\r\nGET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent:test\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 200 OK")
+ s.AssertContains(resp, "<html>", "html content not found")
+}
+
+func HttpInvalidTargetSyntaxTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+
+ resp, err := TcpSendReceive(serverAddress+":80", "GET /interface|stats.json HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "'|' not allowed in target path")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /interface#stats.json HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "'#' not allowed in target path")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /interface%stats.json HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "after '%' there must be two hex-digit characters in target path")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /interface%1stats.json HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "after '%' there must be two hex-digit characters in target path")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /interface%Bstats.json HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "after '%' there must be two hex-digit characters in target path")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /interface%stats.json%B HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "after '%' there must be two hex-digit characters in target path")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /version.json?verbose>true HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "'>' not allowed in target query")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /version.json?verbose%true HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "after '%' there must be two hex-digit characters in target query")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /version.json?verbose=%1 HTTP/1.1\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "after '%' there must be two hex-digit characters in target query")
+}
+
+func HttpInvalidContentLengthTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ resp, err := TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nContent-Length:\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "Content-Length value must be present")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nContent-Length: \r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "Content-Length value must be present")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nContent-Length: a\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request",
+ "Content-Length value other than digit not allowed")
+}
+
+func HttpContentLengthTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
+ ifName := s.VppIfName()
+
+ resp, err := TcpSendReceive(serverAddress+":80",
+ "POST /interface_stats.json HTTP/1.1\r\nContent-Length:4\r\n\r\n"+ifName)
+ s.AssertNil(err, fmt.Sprint(err))
+ validatePostInterfaceStats(s, resp)
+
+ resp, err = TcpSendReceive(serverAddress+":80",
+ "POST /interface_stats.json HTTP/1.1\r\n Content-Length: 4 \r\n\r\n"+ifName)
+ s.AssertNil(err, fmt.Sprint(err))
+ validatePostInterfaceStats(s, resp)
+
+ resp, err = TcpSendReceive(serverAddress+":80",
+ "POST /interface_stats.json HTTP/1.1\r\n\tContent-Length:\t\t4\r\n\r\n"+ifName)
+ s.AssertNil(err, fmt.Sprint(err))
+ validatePostInterfaceStats(s, resp)
+}
+
+func HttpMethodNotImplementedTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("OPTIONS", "http://"+serverAddress+":80/show/version", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 501)
+ s.AssertHttpHeaderNotPresent(resp, "Content-Type")
+ s.AssertHttpContentLength(resp, int64(0))
+}
+
+func HttpVersionNotSupportedTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ resp, err := TcpSendReceive(serverAddress+":80", "GET / HTTP/2\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 505 HTTP Version Not Supported")
+}
+
+func HttpUriDecodeTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/sh%6fw%20versio%6E%20verbose", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ data, err := io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotContains(string(data), "unknown input")
+ s.AssertContains(string(data), "Compiler")
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "text/html")
+}
+
+func HttpHeadersTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ resp, err := TcpSendReceive(
+ serverAddress+":80",
+ "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent:test\r\nAccept:text/xml\r\nAccept:\ttext/plain\t \r\nAccept:text/html\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 200 OK")
+ s.AssertContains(resp, "Content-Type: text/plain")
+ s.AssertNotContains(resp, "<html>", "html content received instead of plain text")
+}
+
+func HttpInvalidHeadersTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ resp, err := TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nUser-Agent: test\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "Header section must end with CRLF CRLF")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser@Agent:test\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "'@' not allowed in field name")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "incomplete field line not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\n: test\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "empty field name not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\rUser-Agent:test\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid field line end not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\nUser-Agent:test\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "invalid field line end not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent:\r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "empty field value not allowed")
+
+ resp, err = TcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent: \r\n\r\n")
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertContains(resp, "HTTP/1.1 400 Bad Request", "empty field value not allowed")
+}
+
+func HeaderServerTest(s *NoTopoSuite) {
+ vpp := s.GetContainerByName("vpp").VppInstance
+ serverAddress := s.VppAddr()
+ vpp.Vppctl("http cli server")
+
+ client := NewHttpClient()
+ req, err := http.NewRequest("GET", "http://"+serverAddress+":80/show/version", nil)
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.Log(DumpHttpResp(resp, true))
+ s.AssertHttpStatus(resp, 200)
+ s.AssertHttpHeaderWithValue(resp, "Server", "http_cli_server")
+ s.AssertHttpHeaderWithValue(resp, "Content-Type", "text/html")
}
diff --git a/extras/hs-test/address_allocator.go b/extras/hs-test/infra/address_allocator.go
index e05ea76b9bb..cb647024412 100644
--- a/extras/hs-test/address_allocator.go
+++ b/extras/hs-test/infra/address_allocator.go
@@ -1,4 +1,4 @@
-package main
+package hst
import (
"errors"
@@ -84,7 +84,7 @@ func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddress
return address, nil
}
-func (a *Ip4AddressAllocator) deleteIpAddresses() {
+func (a *Ip4AddressAllocator) DeleteIpAddresses() {
for ip := range a.assignedIps {
os.Remove(a.assignedIps[ip])
}
diff --git a/extras/hs-test/infra/container.go b/extras/hs-test/infra/container.go
new file mode 100644
index 00000000000..974d1547c03
--- /dev/null
+++ b/extras/hs-test/infra/container.go
@@ -0,0 +1,537 @@
+package hst
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "github.com/docker/go-units"
+ "os"
+ "os/exec"
+ "regexp"
+ "slices"
+ "strconv"
+ "strings"
+ "text/template"
+ "time"
+
+ "github.com/cilium/cilium/pkg/sysctl"
+ containerTypes "github.com/docker/docker/api/types/container"
+ "github.com/docker/docker/api/types/filters"
+ "github.com/docker/docker/api/types/image"
+ "github.com/docker/docker/pkg/stdcopy"
+ "github.com/edwarnicke/exechelper"
+ . "github.com/onsi/ginkgo/v2"
+)
+
+const (
+ logDir string = "/tmp/hs-test/"
+ volumeDir string = "/volumes"
+)
+
+var (
+ workDir, _ = os.Getwd()
+)
+
+type Volume struct {
+ HostDir string
+ ContainerDir string
+ IsDefaultWorkDir bool
+}
+
+type Container struct {
+ Suite *HstSuite
+ IsOptional bool
+ RunDetached bool
+ Name string
+ ID string
+ Image string
+ ExtraRunningArgs string
+ Volumes map[string]Volume
+ EnvVars map[string]string
+ VppInstance *VppInstance
+ AllocatedCpus []int
+ ctx context.Context
+}
+
+func newContainer(suite *HstSuite, yamlInput ContainerConfig) (*Container, error) {
+ containerName := yamlInput["name"].(string)
+ if len(containerName) == 0 {
+ err := fmt.Errorf("container name must not be blank")
+ return nil, err
+ }
+
+ var container = new(Container)
+ container.Volumes = make(map[string]Volume)
+ container.EnvVars = make(map[string]string)
+ container.Name = containerName
+ container.Suite = suite
+ container.ctx = context.Background()
+
+ if Image, ok := yamlInput["image"]; ok {
+ container.Image = Image.(string)
+ } else {
+ container.Image = "hs-test/vpp"
+ }
+
+ if args, ok := yamlInput["extra-args"]; ok {
+ container.ExtraRunningArgs = args.(string)
+ } else {
+ container.ExtraRunningArgs = ""
+ }
+
+ if isOptional, ok := yamlInput["is-optional"]; ok {
+ container.IsOptional = isOptional.(bool)
+ } else {
+ container.IsOptional = false
+ }
+
+ if runDetached, ok := yamlInput["run-detached"]; ok {
+ container.RunDetached = runDetached.(bool)
+ } else {
+ container.RunDetached = true
+ }
+
+ if _, ok := yamlInput["volumes"]; ok {
+ workingVolumeDir := logDir + suite.GetCurrentTestName() + volumeDir
+ workDirReplacer := strings.NewReplacer("$HST_DIR", workDir)
+ volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir)
+ for _, volu := range yamlInput["volumes"].([]interface{}) {
+ volumeMap := volu.(ContainerConfig)
+ hostDir := workDirReplacer.Replace(volumeMap["host-dir"].(string))
+ hostDir = volDirReplacer.Replace(hostDir)
+ containerDir := volumeMap["container-dir"].(string)
+ isDefaultWorkDir := false
+
+ if isDefault, ok := volumeMap["is-default-work-dir"]; ok {
+ isDefaultWorkDir = isDefault.(bool)
+ }
+ container.addVolume(hostDir, containerDir, isDefaultWorkDir)
+ }
+ }
+
+ if _, ok := yamlInput["vars"]; ok {
+ for _, envVar := range yamlInput["vars"].([]interface{}) {
+ envVarMap := envVar.(ContainerConfig)
+ name := envVarMap["name"].(string)
+ value := envVarMap["value"].(string)
+ container.AddEnvVar(name, value)
+ }
+ }
+ return container, nil
+}
+
+func (c *Container) getWorkDirVolume() (res Volume, exists bool) {
+ for _, v := range c.Volumes {
+ if v.IsDefaultWorkDir {
+ res = v
+ exists = true
+ return
+ }
+ }
+ return
+}
+
+func (c *Container) GetHostWorkDir() (res string) {
+ if v, ok := c.getWorkDirVolume(); ok {
+ res = v.HostDir
+ }
+ return
+}
+
+func (c *Container) GetContainerWorkDir() (res string) {
+ if v, ok := c.getWorkDirVolume(); ok {
+ res = v.ContainerDir
+ }
+ return
+}
+
+func (c *Container) getContainerArguments() string {
+ args := "--ulimit nofile=90000:90000 --cap-add=all --privileged --network host"
+ args += c.getVolumesAsCliOption()
+ args += c.getEnvVarsAsCliOption()
+ if *VppSourceFileDir != "" {
+ args += fmt.Sprintf(" -v %s:%s", *VppSourceFileDir, *VppSourceFileDir)
+ }
+ args += " --name " + c.Name + " " + c.Image
+ args += " " + c.ExtraRunningArgs
+ return args
+}
+
+func (c *Container) PullDockerImage(name string, ctx context.Context) {
+ // "func (*Client) ImagePull" doesn't work, returns "No such image"
+ c.Suite.Log("Pulling image: " + name)
+ _, err := exechelper.CombinedOutput("docker pull " + name)
+ c.Suite.AssertNil(err)
+}
+
+// Creates a container
+func (c *Container) Create() error {
+ var sliceOfImageNames []string
+ images, err := c.Suite.Docker.ImageList(c.ctx, image.ListOptions{})
+ c.Suite.AssertNil(err)
+
+ for _, image := range images {
+ sliceOfImageNames = append(sliceOfImageNames, strings.Split(image.RepoTags[0], ":")[0])
+ }
+ if !slices.Contains(sliceOfImageNames, c.Image) {
+ c.PullDockerImage(c.Image, c.ctx)
+ }
+
+ c.allocateCpus()
+ cpuSet := fmt.Sprintf("%d-%d", c.AllocatedCpus[0], c.AllocatedCpus[len(c.AllocatedCpus)-1])
+ resp, err := c.Suite.Docker.ContainerCreate(
+ c.ctx,
+ &containerTypes.Config{
+ Hostname: c.Name,
+ Image: c.Image,
+ Env: c.getEnvVars(),
+ Cmd: strings.Split(c.ExtraRunningArgs, " "),
+ },
+ &containerTypes.HostConfig{
+ Resources: containerTypes.Resources{
+ Ulimits: []*units.Ulimit{
+ {
+ Name: "nofile",
+ Soft: 90000,
+ Hard: 90000,
+ },
+ },
+ CpusetCpus: cpuSet,
+ },
+ CapAdd: []string{"ALL"},
+ Privileged: true,
+ NetworkMode: "host",
+ Binds: c.getVolumesAsSlice(),
+ },
+ nil,
+ nil,
+ c.Name,
+ )
+ c.ID = resp.ID
+ return err
+}
+
+func (c *Container) allocateCpus() {
+ c.Suite.StartedContainers = append(c.Suite.StartedContainers, c)
+ c.AllocatedCpus = c.Suite.AllocateCpus()
+ c.Suite.Log("Allocated CPUs " + fmt.Sprint(c.AllocatedCpus) + " to container " + c.Name)
+}
+
+// Starts a container
+func (c *Container) Start() error {
+ var err error
+ var nTries int
+
+ for nTries = 0; nTries < 5; nTries++ {
+ err = c.Suite.Docker.ContainerStart(c.ctx, c.ID, containerTypes.StartOptions{})
+ if err == nil {
+ continue
+ }
+ c.Suite.Log("Error while starting " + c.Name + ". Retrying...")
+ time.Sleep(1 * time.Second)
+ }
+ if nTries >= 5 {
+ return err
+ }
+
+ // wait for container to start
+ time.Sleep(1 * time.Second)
+
+ // check if container exited right after startup
+ containers, err := c.Suite.Docker.ContainerList(c.ctx, containerTypes.ListOptions{
+ All: true,
+ Filters: filters.NewArgs(filters.Arg("name", c.Name)),
+ })
+ if err != nil {
+ return err
+ }
+ if containers[0].State == "exited" {
+ c.Suite.Log("Container details: " + fmt.Sprint(containers[0]))
+ return fmt.Errorf("Container %s exited: '%s'", c.Name, containers[0].Status)
+ }
+
+ return err
+}
+
+func (c *Container) GetOutput() (string, string) {
+ // Wait for the container to finish executing
+ statusCh, errCh := c.Suite.Docker.ContainerWait(c.ctx, c.ID, containerTypes.WaitConditionNotRunning)
+ select {
+ case err := <-errCh:
+ c.Suite.AssertNil(err)
+ case <-statusCh:
+ }
+
+ // Get the logs from the container
+ logOptions := containerTypes.LogsOptions{ShowStdout: true, ShowStderr: true}
+ logReader, err := c.Suite.Docker.ContainerLogs(c.ctx, c.ID, logOptions)
+ c.Suite.AssertNil(err)
+ defer logReader.Close()
+
+ var stdoutBuf, stderrBuf bytes.Buffer
+
+ // Use stdcopy.StdCopy to demultiplex the multiplexed stream
+ _, err = stdcopy.StdCopy(&stdoutBuf, &stderrBuf, logReader)
+ c.Suite.AssertNil(err)
+
+ stdout := stdoutBuf.String()
+ stderr := stderrBuf.String()
+ return stdout, stderr
+}
+
+func (c *Container) prepareCommand() (string, error) {
+ if c.Name == "" {
+ return "", fmt.Errorf("run container failed: name is blank")
+ }
+
+ cmd := "docker exec "
+ if c.RunDetached {
+ cmd += " -d"
+ }
+
+ cmd += " " + c.getContainerArguments()
+
+ c.Suite.Log(cmd)
+ return cmd, nil
+}
+
+func (c *Container) CombinedOutput() (string, error) {
+ cmd, err := c.prepareCommand()
+ if err != nil {
+ return "", err
+ }
+
+ byteOutput, err := exechelper.CombinedOutput(cmd)
+ return string(byteOutput), err
+}
+
+// Creates and starts a container
+func (c *Container) Run() {
+ c.Suite.AssertNil(c.Create())
+ c.Suite.AssertNil(c.Start())
+}
+
+func (c *Container) addVolume(hostDir string, containerDir string, isDefaultWorkDir bool) {
+ var volume Volume
+ volume.HostDir = strings.Replace(hostDir, "volumes", c.Suite.GetTestId()+"/"+"volumes", 1)
+ volume.ContainerDir = containerDir
+ volume.IsDefaultWorkDir = isDefaultWorkDir
+ c.Volumes[hostDir] = volume
+}
+
+func (c *Container) getVolumesAsSlice() []string {
+ var volumeSlice []string
+
+ if *VppSourceFileDir != "" {
+ volumeSlice = append(volumeSlice, fmt.Sprintf("%s:%s", *VppSourceFileDir, *VppSourceFileDir))
+ }
+
+ core_pattern, err := sysctl.Read("kernel.core_pattern")
+ if err == nil {
+ index := strings.LastIndex(core_pattern, "/")
+ core_pattern = core_pattern[:index]
+ volumeSlice = append(volumeSlice, c.Suite.getLogDirPath()+":"+core_pattern)
+ } else {
+ c.Suite.Log(err)
+ }
+
+ if len(c.Volumes) > 0 {
+ for _, volume := range c.Volumes {
+ volumeSlice = append(volumeSlice, fmt.Sprintf("%s:%s", volume.HostDir, volume.ContainerDir))
+ }
+ }
+ return volumeSlice
+}
+
+func (c *Container) getVolumesAsCliOption() string {
+ cliOption := ""
+
+ if len(c.Volumes) > 0 {
+ for _, volume := range c.Volumes {
+ cliOption += fmt.Sprintf(" -v %s:%s", volume.HostDir, volume.ContainerDir)
+ }
+ }
+
+ return cliOption
+}
+
+func (c *Container) AddEnvVar(name string, value string) {
+ c.EnvVars[name] = value
+}
+
+func (c *Container) getEnvVarsAsCliOption() string {
+ cliOption := ""
+ if len(c.EnvVars) == 0 {
+ return cliOption
+ }
+
+ for name, value := range c.EnvVars {
+ cliOption += fmt.Sprintf(" -e %s=%s", name, value)
+ }
+ return cliOption
+}
+
+func (c *Container) getEnvVars() []string {
+ var envVars []string
+ if len(c.EnvVars) == 0 {
+ return envVars
+ }
+
+ for name, value := range c.EnvVars {
+ envVars = append(envVars, fmt.Sprintf("%s=%s", name, value))
+ }
+ return envVars
+}
+
+func (c *Container) newVppInstance(cpus []int, additionalConfigs ...Stanza) (*VppInstance, error) {
+ vpp := new(VppInstance)
+ vpp.Container = c
+ vpp.Cpus = cpus
+ vpp.setDefaultCpuConfig()
+ vpp.AdditionalConfig = append(vpp.AdditionalConfig, additionalConfigs...)
+ c.VppInstance = vpp
+ return vpp, nil
+}
+
+func (c *Container) copy(sourceFileName string, targetFileName string) error {
+ cmd := exec.Command("docker", "cp", sourceFileName, c.Name+":"+targetFileName)
+ return cmd.Run()
+}
+
+func (c *Container) CreateFile(destFileName string, content string) error {
+ f, err := os.CreateTemp("/tmp", "hst-config"+c.Suite.Ppid)
+ if err != nil {
+ return err
+ }
+ defer os.Remove(f.Name())
+
+ if _, err := f.Write([]byte(content)); err != nil {
+ return err
+ }
+ if err := f.Close(); err != nil {
+ return err
+ }
+ c.copy(f.Name(), destFileName)
+ return nil
+}
+
+func (c *Container) GetFile(sourceFileName, targetFileName string) error {
+ cmd := exec.Command("docker", "cp", c.Name+":"+sourceFileName, targetFileName)
+ return cmd.Run()
+}
+
+/*
+ * Executes in detached mode so that the started application can continue to run
+ * without blocking execution of test
+ */
+func (c *Container) ExecServer(command string, arguments ...any) {
+ serverCommand := fmt.Sprintf(command, arguments...)
+ containerExecCommand := "docker exec -d" + c.getEnvVarsAsCliOption() +
+ " " + c.Name + " " + serverCommand
+ GinkgoHelper()
+ c.Suite.Log(containerExecCommand)
+ c.Suite.AssertNil(exechelper.Run(containerExecCommand))
+}
+
+func (c *Container) Exec(command string, arguments ...any) string {
+ cliCommand := fmt.Sprintf(command, arguments...)
+ containerExecCommand := "docker exec" + c.getEnvVarsAsCliOption() +
+ " " + c.Name + " " + cliCommand
+ GinkgoHelper()
+ c.Suite.Log(containerExecCommand)
+ byteOutput, err := exechelper.CombinedOutput(containerExecCommand)
+ c.Suite.AssertNil(err, fmt.Sprint(err))
+ return string(byteOutput)
+}
+
+func (c *Container) saveLogs() {
+ testLogFilePath := c.Suite.getLogDirPath() + "container-" + c.Name + ".log"
+
+ logs, err := c.log(0)
+ if err != nil {
+ c.Suite.Log(err)
+ return
+ }
+
+ f, err := os.Create(testLogFilePath)
+ if err != nil {
+ c.Suite.Log(err)
+ return
+ }
+ defer f.Close()
+ fmt.Fprint(f, logs)
+}
+
+// Returns logs from docker containers. Set 'maxLines' to 0 to output the full log.
+func (c *Container) log(maxLines int) (string, error) {
+ var logOptions containerTypes.LogsOptions
+ if maxLines == 0 {
+ logOptions = containerTypes.LogsOptions{ShowStdout: true, ShowStderr: true, Details: true, Timestamps: true}
+ } else {
+ logOptions = containerTypes.LogsOptions{ShowStdout: true, ShowStderr: true, Details: true, Tail: strconv.Itoa(maxLines)}
+ }
+
+ out, err := c.Suite.Docker.ContainerLogs(c.ctx, c.ID, logOptions)
+ if err != nil {
+ c.Suite.Log(err)
+ return "", err
+ }
+ defer out.Close()
+
+ var stdoutBuf, stderrBuf bytes.Buffer
+
+ _, err = stdcopy.StdCopy(&stdoutBuf, &stderrBuf, out)
+ if err != nil {
+ c.Suite.Log(err)
+ }
+
+ stdout := stdoutBuf.String()
+ stderr := stderrBuf.String()
+
+ re := regexp.MustCompile("(?m)^.*==> /dev/null <==.*$[\r\n]+")
+ stdout = re.ReplaceAllString(stdout, "")
+
+ re = regexp.MustCompile("(?m)^.*tail: cannot open '' for reading: No such file or directory.*$[\r\n]+")
+ stderr = re.ReplaceAllString(stderr, "")
+
+ return stdout + stderr, err
+}
+
+func (c *Container) stop() error {
+ if c.VppInstance != nil && c.VppInstance.ApiStream != nil {
+ c.VppInstance.saveLogs()
+ c.VppInstance.Disconnect()
+ }
+ c.VppInstance = nil
+ c.saveLogs()
+
+ c.Suite.Log("Stopping container " + c.Name)
+ timeout := 0
+ if err := c.Suite.Docker.ContainerStop(c.ctx, c.ID, containerTypes.StopOptions{Timeout: &timeout}); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *Container) CreateConfig(targetConfigName string, templateName string, values any) {
+ template := template.Must(template.ParseFiles(templateName))
+
+ f, err := os.CreateTemp(logDir, "hst-config")
+ c.Suite.AssertNil(err, err)
+ defer os.Remove(f.Name())
+
+ err = template.Execute(f, values)
+ c.Suite.AssertNil(err, err)
+
+ err = f.Close()
+ c.Suite.AssertNil(err, err)
+
+ c.copy(f.Name(), targetConfigName)
+}
+
+func init() {
+ cmd := exec.Command("mkdir", "-p", logDir)
+ if err := cmd.Run(); err != nil {
+ panic(err)
+ }
+}
diff --git a/extras/hs-test/infra/cpu.go b/extras/hs-test/infra/cpu.go
new file mode 100644
index 00000000000..a1682819a2f
--- /dev/null
+++ b/extras/hs-test/infra/cpu.go
@@ -0,0 +1,209 @@
+package hst
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ . "github.com/onsi/ginkgo/v2"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+)
+
+var CgroupPath = "/sys/fs/cgroup/"
+
+type CpuContext struct {
+ cpuAllocator *CpuAllocatorT
+ cpus []int
+}
+
+type CpuAllocatorT struct {
+ cpus []int
+ runningInCi bool
+ buildNumber int
+ maxContainerCount int
+}
+
+func iterateAndAppend(start int, end int, slice []int) []int {
+ for i := start; i <= end; i++ {
+ slice = append(slice, i)
+ }
+ return slice
+}
+
+var cpuAllocator *CpuAllocatorT = nil
+
+func (c *CpuAllocatorT) Allocate(containerCount int, nCpus int) (*CpuContext, error) {
+ var cpuCtx CpuContext
+ // indexes, not actual cores
+ var minCpu, maxCpu int
+
+ if c.runningInCi {
+ minCpu = ((c.buildNumber) * c.maxContainerCount * nCpus)
+ maxCpu = ((c.buildNumber + 1) * c.maxContainerCount * nCpus) - 1
+ } else {
+ minCpu = ((GinkgoParallelProcess() - 1) * c.maxContainerCount * nCpus)
+ maxCpu = (GinkgoParallelProcess() * c.maxContainerCount * nCpus) - 1
+ }
+
+ if len(c.cpus)-1 < maxCpu {
+ err := fmt.Errorf("could not allocate %d CPUs; available count: %d; attempted to allocate cores with index %d-%d; max index: %d;\n"+
+ "available cores: %v", nCpus*containerCount, len(c.cpus), minCpu, maxCpu, len(c.cpus)-1, c.cpus)
+ return nil, err
+ }
+
+ if containerCount == 1 {
+ cpuCtx.cpus = c.cpus[minCpu : minCpu+nCpus]
+ } else if containerCount > 1 && containerCount <= c.maxContainerCount {
+ cpuCtx.cpus = c.cpus[minCpu+(nCpus*(containerCount-1)) : minCpu+(nCpus*containerCount)]
+ } else {
+ return nil, fmt.Errorf("too many containers; CPU allocation for >%d containers is not implemented", c.maxContainerCount)
+ }
+ cpuCtx.cpuAllocator = c
+ return &cpuCtx, nil
+}
+
+func (c *CpuAllocatorT) readCpus() error {
+ var first, second, third, fourth int
+ var file *os.File
+ var err error
+
+ if c.runningInCi {
+ // non-debug build runs on node0, debug on node1
+ if *IsDebugBuild {
+ file, err = os.Open("/sys/devices/system/node/node1/cpulist")
+ } else {
+ file, err = os.Open("/sys/devices/system/node/node0/cpulist")
+ }
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ sc := bufio.NewScanner(file)
+ sc.Scan()
+ line := sc.Text()
+ _, err = fmt.Sscanf(line, "%d-%d,%d-%d", &first, &second, &third, &fourth)
+ if err != nil {
+ return err
+ }
+
+ c.cpus = iterateAndAppend(first, second, c.cpus)
+ c.cpus = iterateAndAppend(third, fourth, c.cpus)
+ } else if NumaAwareCpuAlloc {
+ var fifth, sixth int
+ var tmpCpus []int
+
+ file, err := os.Open("/sys/devices/system/node/online")
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ sc := bufio.NewScanner(file)
+ sc.Scan()
+ line := sc.Text()
+ // get numa node range
+ _, err = fmt.Sscanf(line, "%d-%d", &first, &second)
+ if err != nil {
+ return err
+ }
+
+ for i := first; i <= second; i++ {
+ file, err := os.Open("/sys/devices/system/node/node" + fmt.Sprint(i) + "/cpulist")
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ // get numa node cores
+ sc := bufio.NewScanner(file)
+ sc.Scan()
+ line := sc.Text()
+ _, err = fmt.Sscanf(line, "%d-%d,%d-%d", &third, &fourth, &fifth, &sixth)
+ if err != nil {
+ return err
+ }
+
+ // get numa node cores from first range
+ tmpCpus = iterateAndAppend(third, fourth, tmpCpus)
+
+ // discard cpu 0
+ if tmpCpus[0] == 0 && !*UseCpu0 {
+ tmpCpus = tmpCpus[1:]
+ }
+
+ // get numa node cores from second range
+ tmpCpus = iterateAndAppend(fifth, sixth, tmpCpus)
+
+ // make c.cpus divisible by maxContainerCount * nCpus, so we don't have to check which numa will be used
+ // and we can use offsets
+ count_to_remove := len(tmpCpus) % (c.maxContainerCount * *NConfiguredCpus)
+ c.cpus = append(c.cpus, tmpCpus[:len(tmpCpus)-count_to_remove]...)
+ tmpCpus = tmpCpus[:0]
+ }
+ } else {
+ // Path depends on cgroup version. We need to check which version is in use.
+ // For that following command can be used: 'stat -fc %T /sys/fs/cgroup/'
+ // In case the output states 'cgroup2fs' then cgroups v2 is used, 'tmpfs' in case cgroups v1.
+ cmd := exec.Command("stat", "-fc", "%T", "/sys/fs/cgroup/")
+ byteOutput, err := cmd.CombinedOutput()
+ if err != nil {
+ return err
+ }
+
+ CpuPath := CgroupPath
+ if strings.Contains(string(byteOutput), "tmpfs") {
+ CpuPath += "cpuset/cpuset.effective_cpus"
+ } else if strings.Contains(string(byteOutput), "cgroup2fs") {
+ CpuPath += "cpuset.cpus.effective"
+ } else {
+ return errors.New("cgroup unknown fs: " + string(byteOutput))
+ }
+
+ file, err := os.Open(CpuPath)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ sc := bufio.NewScanner(file)
+ sc.Scan()
+ line := sc.Text()
+ _, err = fmt.Sscanf(line, "%d-%d", &first, &second)
+ if err != nil {
+ return err
+ }
+ c.cpus = iterateAndAppend(first, second, c.cpus)
+ }
+
+ // discard cpu 0
+ if c.cpus[0] == 0 && !*UseCpu0 {
+ c.cpus = c.cpus[1:]
+ }
+ return nil
+}
+
+func CpuAllocator() (*CpuAllocatorT, error) {
+ if cpuAllocator == nil {
+ var err error
+ cpuAllocator = new(CpuAllocatorT)
+ cpuAllocator.maxContainerCount = 4
+ buildNumberStr := os.Getenv("BUILD_NUMBER")
+
+ if buildNumberStr != "" {
+ cpuAllocator.runningInCi = true
+ // get last digit of build number
+ cpuAllocator.buildNumber, err = strconv.Atoi(buildNumberStr[len(buildNumberStr)-1:])
+ if err != nil {
+ return nil, err
+ }
+ }
+ err = cpuAllocator.readCpus()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return cpuAllocator, nil
+}
diff --git a/extras/hs-test/infra/hst_suite.go b/extras/hs-test/infra/hst_suite.go
new file mode 100644
index 00000000000..ed8da3fe244
--- /dev/null
+++ b/extras/hs-test/infra/hst_suite.go
@@ -0,0 +1,642 @@
+package hst
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "github.com/edwarnicke/exechelper"
+ "io"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+
+ containerTypes "github.com/docker/docker/api/types/container"
+ "github.com/docker/docker/client"
+ "github.com/onsi/gomega/gmeasure"
+ "gopkg.in/yaml.v3"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+const (
+ DEFAULT_NETWORK_NUM int = 1
+)
+
+var IsPersistent = flag.Bool("persist", false, "persists topology config")
+var IsVerbose = flag.Bool("verbose", false, "verbose test output")
+var IsUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
+var IsVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
+var NConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp")
+var VppSourceFileDir = flag.String("vppsrc", "", "vpp source file directory")
+var IsDebugBuild = flag.Bool("debug_build", false, "some paths are different with debug build")
+var UseCpu0 = flag.Bool("cpu0", false, "use cpu0")
+var IsLeakCheck = flag.Bool("leak_check", false, "run leak-check tests")
+var ParallelTotal = flag.Lookup("ginkgo.parallel.total")
+var NumaAwareCpuAlloc bool
+var SuiteTimeout time.Duration
+
+type HstSuite struct {
+ Containers map[string]*Container
+ StartedContainers []*Container
+ Volumes []string
+ NetConfigs []NetConfig
+ NetInterfaces map[string]*NetInterface
+ Ip4AddrAllocator *Ip4AddressAllocator
+ TestIds map[string]string
+ CpuAllocator *CpuAllocatorT
+ CpuContexts []*CpuContext
+ CpuCount int
+ Ppid string
+ ProcessIndex string
+ Logger *log.Logger
+ LogFile *os.File
+ Docker *client.Client
+}
+
+// used for colorful ReportEntry
+type StringerStruct struct {
+ Label string
+}
+
+// ColorableString for ReportEntry to use
+func (s StringerStruct) ColorableString() string {
+ return fmt.Sprintf("{{red}}%s{{/}}", s.Label)
+}
+
+// non-colorable String() is used by go's string formatting support but ignored by ReportEntry
+func (s StringerStruct) String() string {
+ return s.Label
+}
+
+func getTestFilename() string {
+ _, filename, _, _ := runtime.Caller(2)
+ return filepath.Base(filename)
+}
+
+func (s *HstSuite) getLogDirPath() string {
+ testId := s.GetTestId()
+ testName := s.GetCurrentTestName()
+ logDirPath := logDir + testName + "/" + testId + "/"
+
+ cmd := exec.Command("mkdir", "-p", logDirPath)
+ if err := cmd.Run(); err != nil {
+ Fail("mkdir error: " + fmt.Sprint(err))
+ }
+
+ return logDirPath
+}
+
+func (s *HstSuite) newDockerClient() {
+ var err error
+ s.Docker, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
+ s.AssertNil(err)
+ s.Log("docker client created")
+}
+
+func (s *HstSuite) SetupSuite() {
+ s.CreateLogger()
+ s.newDockerClient()
+ s.Log("Suite Setup")
+ RegisterFailHandler(func(message string, callerSkip ...int) {
+ s.HstFail()
+ Fail(message, callerSkip...)
+ })
+ var err error
+ s.Ppid = fmt.Sprint(os.Getppid())
+ // remove last number so we have space to prepend a process index (interfaces have a char limit)
+ s.Ppid = s.Ppid[:len(s.Ppid)-1]
+ s.ProcessIndex = fmt.Sprint(GinkgoParallelProcess())
+ s.CpuAllocator, err = CpuAllocator()
+ if err != nil {
+ Fail("failed to init cpu allocator: " + fmt.Sprint(err))
+ }
+ s.CpuCount = *NConfiguredCpus
+}
+
+func (s *HstSuite) AllocateCpus() []int {
+ cpuCtx, err := s.CpuAllocator.Allocate(len(s.StartedContainers), s.CpuCount)
+ // using Fail instead of AssertNil to make error message more readable
+ if err != nil {
+ Fail(fmt.Sprint(err))
+ }
+ s.AddCpuContext(cpuCtx)
+ return cpuCtx.cpus
+}
+
+func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
+ s.CpuContexts = append(s.CpuContexts, cpuCtx)
+}
+
+func (s *HstSuite) TearDownSuite() {
+ defer s.LogFile.Close()
+ defer s.Docker.Close()
+ s.Log("Suite Teardown")
+ s.UnconfigureNetworkTopology()
+}
+
+func (s *HstSuite) TearDownTest() {
+ s.Log("Test Teardown")
+ if *IsPersistent {
+ return
+ }
+ s.WaitForCoreDump()
+ s.ResetContainers()
+
+ if s.Ip4AddrAllocator != nil {
+ s.Ip4AddrAllocator.DeleteIpAddresses()
+ }
+}
+
+func (s *HstSuite) SkipIfUnconfiguring() {
+ if *IsUnconfiguring {
+ s.Skip("skipping to unconfigure")
+ }
+}
+
+func (s *HstSuite) SetupTest() {
+ s.Log("Test Setup")
+ s.StartedContainers = s.StartedContainers[:0]
+ s.SkipIfUnconfiguring()
+ s.SetupContainers()
+}
+
+func (s *HstSuite) SetupContainers() {
+ for _, container := range s.Containers {
+ if !container.IsOptional {
+ container.Run()
+ }
+ }
+}
+
+func (s *HstSuite) LogVppInstance(container *Container, maxLines int) {
+ if container.VppInstance == nil {
+ return
+ }
+
+ logSource := container.GetHostWorkDir() + defaultLogFilePath
+ file, err := os.Open(logSource)
+
+ if err != nil {
+ return
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+ var lines []string
+ var counter int
+
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ counter++
+ if counter > maxLines {
+ lines = lines[1:]
+ counter--
+ }
+ }
+
+ s.Log("vvvvvvvvvvvvvvv " + container.Name + " [VPP instance]:")
+ for _, line := range lines {
+ s.Log(line)
+ }
+ s.Log("^^^^^^^^^^^^^^^\n\n")
+}
+
+func (s *HstSuite) HstFail() {
+ for _, container := range s.StartedContainers {
+ out, err := container.log(20)
+ if err != nil {
+ s.Log("An error occured while obtaining '" + container.Name + "' container logs: " + fmt.Sprint(err))
+ s.Log("The container might not be running - check logs in " + s.getLogDirPath())
+ continue
+ }
+ s.Log("\nvvvvvvvvvvvvvvv " +
+ container.Name + ":\n" +
+ out +
+ "^^^^^^^^^^^^^^^\n\n")
+ s.LogVppInstance(container, 20)
+ }
+}
+
+func (s *HstSuite) AssertNil(object interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, object).To(BeNil(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotNil(object interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, object).ToNot(BeNil(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, actual).To(Equal(expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, actual).ToNot(Equal(expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, testString).To(ContainSubstring(fmt.Sprint(contains)), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, testString).ToNot(ContainSubstring(fmt.Sprint(contains)), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertEmpty(object interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, object).To(BeEmpty(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, object).ToNot(BeEmpty(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertMatchError(actual, expected error, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, actual).To(MatchError(expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertGreaterThan(actual, expected interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, actual).Should(BeNumerically(">=", expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertTimeEqualWithinThreshold(actual, expected time.Time, threshold time.Duration, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, actual).Should(BeTemporally("~", expected, threshold), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpStatus(resp *http.Response, expectedStatus int, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, resp).To(HaveHTTPStatus(expectedStatus), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpHeaderWithValue(resp *http.Response, key string, value interface{}, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue(key, value), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpHeaderNotPresent(resp *http.Response, key string, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, resp.Header.Get(key)).To(BeEmpty(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpContentLength(resp *http.Response, expectedContentLen int64, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue("Content-Length", strconv.FormatInt(expectedContentLen, 10)), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpBody(resp *http.Response, expectedBody string, msgAndArgs ...interface{}) {
+ ExpectWithOffset(2, resp).To(HaveHTTPBody(expectedBody), msgAndArgs...)
+}
+
+func (s *HstSuite) CreateLogger() {
+ suiteName := s.GetCurrentSuiteName()
+ var err error
+ s.LogFile, err = os.Create("summary/" + suiteName + ".log")
+ if err != nil {
+ Fail("Unable to create log file.")
+ }
+ s.Logger = log.New(io.Writer(s.LogFile), "", log.LstdFlags)
+}
+
+// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter
+// to keep console tidy
+func (s *HstSuite) Log(arg any) {
+ logs := strings.Split(fmt.Sprint(arg), "\n")
+ for _, line := range logs {
+ s.Logger.Println(line)
+ }
+ if *IsVerbose {
+ GinkgoWriter.Println(arg)
+ }
+}
+
+func (s *HstSuite) Skip(args string) {
+ Skip(args)
+}
+
+func (s *HstSuite) SkipIfMultiWorker(args ...any) {
+ if *NConfiguredCpus > 1 {
+ s.Skip("test case not supported with multiple vpp workers")
+ }
+}
+
+func (s *HstSuite) SkipIfNotEnoughAvailableCpus() bool {
+ var MaxRequestedCpu int
+
+ if s.CpuAllocator.runningInCi {
+ MaxRequestedCpu = ((s.CpuAllocator.buildNumber + 1) * s.CpuAllocator.maxContainerCount * s.CpuCount)
+ } else {
+ MaxRequestedCpu = (GinkgoParallelProcess() * s.CpuAllocator.maxContainerCount * s.CpuCount)
+ }
+
+ if len(s.CpuAllocator.cpus)-1 < MaxRequestedCpu {
+ s.Skip(fmt.Sprintf("test case cannot allocate requested cpus (%d cpus * %d containers)", s.CpuCount, s.CpuAllocator.maxContainerCount))
+ }
+
+ return true
+}
+
+func (s *HstSuite) SkipUnlessExtendedTestsBuilt() {
+ imageName := "hs-test/nginx-http3"
+
+ cmd := exec.Command("docker", "images", imageName)
+ byteOutput, err := cmd.CombinedOutput()
+ if err != nil {
+ s.Log("error while searching for docker image")
+ return
+ }
+ if !strings.Contains(string(byteOutput), imageName) {
+ s.Skip("extended tests not built")
+ }
+}
+
+func (s *HstSuite) SkipUnlessLeakCheck() {
+ if !*IsLeakCheck {
+ s.Skip("leak-check tests excluded")
+ }
+}
+
+func (s *HstSuite) WaitForCoreDump() {
+ var filename string
+ dir, err := os.Open(s.getLogDirPath())
+ if err != nil {
+ s.Log(err)
+ return
+ }
+ defer dir.Close()
+
+ files, err := dir.Readdirnames(0)
+ if err != nil {
+ s.Log(err)
+ return
+ }
+ for _, file := range files {
+ if strings.Contains(file, "core") {
+ filename = file
+ }
+ }
+ timeout := 60
+ waitTime := 5
+
+ if filename != "" {
+ corePath := s.getLogDirPath() + filename
+ s.Log(fmt.Sprintf("WAITING FOR CORE DUMP (%s)", corePath))
+ for i := waitTime; i <= timeout; i += waitTime {
+ fileInfo, err := os.Stat(corePath)
+ if err != nil {
+ s.Log("Error while reading file info: " + fmt.Sprint(err))
+ return
+ }
+ currSize := fileInfo.Size()
+ s.Log(fmt.Sprintf("Waiting %ds/%ds...", i, timeout))
+ time.Sleep(time.Duration(waitTime) * time.Second)
+ fileInfo, _ = os.Stat(corePath)
+
+ if currSize == fileInfo.Size() {
+ debug := ""
+ if *IsDebugBuild {
+ debug = "_debug"
+ }
+ vppBinPath := fmt.Sprintf("../../build-root/build-vpp%s-native/vpp/bin/vpp", debug)
+ pluginsLibPath := fmt.Sprintf("build-root/build-vpp%s-native/vpp/lib/x86_64-linux-gnu/vpp_plugins", debug)
+ cmd := fmt.Sprintf("sudo gdb %s -c %s -ex 'set solib-search-path %s/%s' -ex 'bt full' -batch", vppBinPath, corePath, *VppSourceFileDir, pluginsLibPath)
+ s.Log(cmd)
+ output, _ := exechelper.Output(cmd)
+ AddReportEntry("VPP Backtrace", StringerStruct{Label: string(output)})
+ os.WriteFile(s.getLogDirPath()+"backtrace.log", output, os.FileMode(0644))
+ if s.CpuAllocator.runningInCi {
+ err = os.Remove(corePath)
+ if err == nil {
+ s.Log("removed " + corePath)
+ } else {
+ s.Log(err)
+ }
+ }
+ return
+ }
+ }
+ }
+}
+
+func (s *HstSuite) ResetContainers() {
+ for _, container := range s.StartedContainers {
+ container.stop()
+ s.Log("Removing container " + container.Name)
+ if err := s.Docker.ContainerRemove(container.ctx, container.ID, containerTypes.RemoveOptions{RemoveVolumes: true}); err != nil {
+ s.Log(err)
+ }
+ }
+}
+
+func (s *HstSuite) GetNetNamespaceByName(name string) string {
+ return s.ProcessIndex + name + s.Ppid
+}
+
+func (s *HstSuite) GetInterfaceByName(name string) *NetInterface {
+ return s.NetInterfaces[s.ProcessIndex+name+s.Ppid]
+}
+
+func (s *HstSuite) GetContainerByName(name string) *Container {
+ return s.Containers[s.ProcessIndex+name+s.Ppid]
+}
+
+/*
+ * Create a copy and return its address, so that individial tests which call this
+ * are not able to modify the original container and affect other tests by doing that
+ */
+func (s *HstSuite) GetTransientContainerByName(name string) *Container {
+ containerCopy := *s.Containers[s.ProcessIndex+name+s.Ppid]
+ return &containerCopy
+}
+
+func (s *HstSuite) LoadContainerTopology(topologyName string) {
+ data, err := os.ReadFile(containerTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ Fail("read error: " + fmt.Sprint(err))
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ Fail("unmarshal error: " + fmt.Sprint(err))
+ }
+
+ for _, elem := range yamlTopo.Volumes {
+ volumeMap := elem["volume"].(VolumeConfig)
+ hostDir := volumeMap["host-dir"].(string)
+ workingVolumeDir := logDir + s.GetCurrentTestName() + volumeDir
+ volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir)
+ hostDir = volDirReplacer.Replace(hostDir)
+ s.Volumes = append(s.Volumes, hostDir)
+ }
+
+ s.Containers = make(map[string]*Container)
+ for _, elem := range yamlTopo.Containers {
+ newContainer, err := newContainer(s, elem)
+ newContainer.Suite = s
+ newContainer.Name = newContainer.Suite.ProcessIndex + newContainer.Name + newContainer.Suite.Ppid
+ if err != nil {
+ Fail("container config error: " + fmt.Sprint(err))
+ }
+ s.Containers[newContainer.Name] = newContainer
+ }
+}
+
+func (s *HstSuite) LoadNetworkTopology(topologyName string) {
+ data, err := os.ReadFile(networkTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ Fail("read error: " + fmt.Sprint(err))
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ Fail("unmarshal error: " + fmt.Sprint(err))
+ }
+
+ s.Ip4AddrAllocator = NewIp4AddressAllocator()
+ s.NetInterfaces = make(map[string]*NetInterface)
+
+ for _, elem := range yamlTopo.Devices {
+ if _, ok := elem["name"]; ok {
+ elem["name"] = s.ProcessIndex + elem["name"].(string) + s.Ppid
+ }
+
+ if peer, ok := elem["peer"].(NetDevConfig); ok {
+ if peer["name"].(string) != "" {
+ peer["name"] = s.ProcessIndex + peer["name"].(string) + s.Ppid
+ }
+ if _, ok := peer["netns"]; ok {
+ peer["netns"] = s.ProcessIndex + peer["netns"].(string) + s.Ppid
+ }
+ }
+
+ if _, ok := elem["netns"]; ok {
+ elem["netns"] = s.ProcessIndex + elem["netns"].(string) + s.Ppid
+ }
+
+ if _, ok := elem["interfaces"]; ok {
+ interfaceCount := len(elem["interfaces"].([]interface{}))
+ for i := 0; i < interfaceCount; i++ {
+ elem["interfaces"].([]interface{})[i] = s.ProcessIndex + elem["interfaces"].([]interface{})[i].(string) + s.Ppid
+ }
+ }
+
+ switch elem["type"].(string) {
+ case NetNs:
+ {
+ if namespace, err := newNetNamespace(elem); err == nil {
+ s.NetConfigs = append(s.NetConfigs, &namespace)
+ } else {
+ Fail("network config error: " + fmt.Sprint(err))
+ }
+ }
+ case Veth, Tap:
+ {
+ if netIf, err := newNetworkInterface(elem, s.Ip4AddrAllocator); err == nil {
+ s.NetConfigs = append(s.NetConfigs, netIf)
+ s.NetInterfaces[netIf.Name()] = netIf
+ } else {
+ Fail("network config error: " + fmt.Sprint(err))
+ }
+ }
+ case Bridge:
+ {
+ if bridge, err := newBridge(elem); err == nil {
+ s.NetConfigs = append(s.NetConfigs, &bridge)
+ } else {
+ Fail("network config error: " + fmt.Sprint(err))
+ }
+ }
+ }
+ }
+}
+
+func (s *HstSuite) ConfigureNetworkTopology(topologyName string) {
+ s.LoadNetworkTopology(topologyName)
+
+ if *IsUnconfiguring {
+ return
+ }
+
+ for _, nc := range s.NetConfigs {
+ s.Log(nc.Name())
+ if err := nc.configure(); err != nil {
+ Fail("Network config error: " + fmt.Sprint(err))
+ }
+ }
+}
+
+func (s *HstSuite) UnconfigureNetworkTopology() {
+ if *IsPersistent {
+ return
+ }
+ for _, nc := range s.NetConfigs {
+ nc.unconfigure()
+ }
+}
+
+func (s *HstSuite) GetTestId() string {
+ testName := s.GetCurrentTestName()
+
+ if s.TestIds == nil {
+ s.TestIds = map[string]string{}
+ }
+
+ if _, ok := s.TestIds[testName]; !ok {
+ s.TestIds[testName] = time.Now().Format("2006-01-02_15-04-05")
+ }
+
+ return s.TestIds[testName]
+}
+
+func (s *HstSuite) GetCurrentTestName() string {
+ return strings.Split(CurrentSpecReport().LeafNodeText, "/")[1]
+}
+
+func (s *HstSuite) GetCurrentSuiteName() string {
+ return CurrentSpecReport().ContainerHierarchyTexts[0]
+}
+
+// Returns last 3 digits of PID + Ginkgo process index as the 4th digit
+func (s *HstSuite) GetPortFromPpid() string {
+ port := s.Ppid
+ for len(port) < 3 {
+ port += "0"
+ }
+ return port[len(port)-3:] + s.ProcessIndex
+}
+
+/*
+RunBenchmark creates Gomega's experiment with the passed-in name and samples the passed-in callback repeatedly (samplesNum times),
+passing in suite context, experiment and your data.
+
+You can also instruct runBenchmark to run with multiple concurrent workers.
+Note that if running in parallel Gomega returns from Sample when spins up all samples and does not wait until all finished.
+You can record multiple named measurements (float64 or duration) within passed-in callback.
+runBenchmark then produces report to show statistical distribution of measurements.
+*/
+func (s *HstSuite) RunBenchmark(name string, samplesNum, parallelNum int, callback func(s *HstSuite, e *gmeasure.Experiment, data interface{}), data interface{}) {
+ experiment := gmeasure.NewExperiment(name)
+
+ experiment.Sample(func(idx int) {
+ defer GinkgoRecover()
+ callback(s, experiment, data)
+ }, gmeasure.SamplingConfig{N: samplesNum, NumParallel: parallelNum})
+ AddReportEntry(experiment.Name, experiment)
+}
+
+/*
+LogHttpReq is Gomega's ghttp server handler which logs received HTTP request.
+
+You should put it at the first place, so request is logged always.
+*/
+func (s *HstSuite) LogHttpReq(body bool) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ dump, err := httputil.DumpRequest(req, body)
+ if err == nil {
+ s.Log("\n> Received request (" + req.RemoteAddr + "):\n" +
+ string(dump) +
+ "\n------------------------------\n")
+ }
+ }
+}
diff --git a/extras/hs-test/netconfig.go b/extras/hs-test/infra/netconfig.go
index c76a0fda5f5..3f3d3e3e84c 100644
--- a/extras/hs-test/netconfig.go
+++ b/extras/hs-test/infra/netconfig.go
@@ -1,4 +1,4 @@
-package main
+package hst
import (
"errors"
@@ -32,13 +32,13 @@ type (
NetInterface struct {
NetConfigBase
- ip4AddrAllocator *Ip4AddressAllocator
- ip4Address string
- index InterfaceIndex
- hwAddress MacAddress
- networkNamespace string
- networkNumber int
- peer *NetInterface
+ Ip4AddrAllocator *Ip4AddressAllocator
+ Ip4Address string
+ Index InterfaceIndex
+ HwAddress MacAddress
+ NetworkNamespace string
+ NetworkNumber int
+ Peer *NetInterface
}
NetworkNamespace struct {
@@ -47,8 +47,8 @@ type (
NetworkBridge struct {
NetConfigBase
- networkNamespace string
- interfaces []string
+ NetworkNamespace string
+ Interfaces []string
}
)
@@ -64,7 +64,7 @@ type InterfaceAdder func(n *NetInterface) *Cmd
var (
ipCommandMap = map[string]InterfaceAdder{
Veth: func(n *NetInterface) *Cmd {
- return exec.Command("ip", "link", "add", n.name, "type", "veth", "peer", "name", n.peer.name)
+ return exec.Command("ip", "link", "add", n.name, "type", "veth", "peer", "name", n.Peer.name)
},
Tap: func(n *NetInterface) *Cmd {
return exec.Command("ip", "tuntap", "add", n.name, "mode", "tap")
@@ -75,31 +75,31 @@ var (
func newNetworkInterface(cfg NetDevConfig, a *Ip4AddressAllocator) (*NetInterface, error) {
var newInterface *NetInterface = &NetInterface{}
var err error
- newInterface.ip4AddrAllocator = a
+ newInterface.Ip4AddrAllocator = a
newInterface.name = cfg["name"].(string)
- newInterface.networkNumber = DEFAULT_NETWORK_NUM
+ newInterface.NetworkNumber = DEFAULT_NETWORK_NUM
if interfaceType, ok := cfg["type"]; ok {
newInterface.category = interfaceType.(string)
}
if presetHwAddress, ok := cfg["preset-hw-address"]; ok {
- newInterface.hwAddress, err = ethernet_types.ParseMacAddress(presetHwAddress.(string))
+ newInterface.HwAddress, err = ethernet_types.ParseMacAddress(presetHwAddress.(string))
if err != nil {
return &NetInterface{}, err
}
}
if netns, ok := cfg["netns"]; ok {
- newInterface.networkNamespace = netns.(string)
+ newInterface.NetworkNamespace = netns.(string)
}
if ip, ok := cfg["ip4"]; ok {
if n, ok := ip.(NetDevConfig)["network"]; ok {
- newInterface.networkNumber = n.(int)
+ newInterface.NetworkNumber = n.(int)
}
- newInterface.ip4Address, err = newInterface.ip4AddrAllocator.NewIp4InterfaceAddress(
- newInterface.networkNumber,
+ newInterface.Ip4Address, err = newInterface.Ip4AddrAllocator.NewIp4InterfaceAddress(
+ newInterface.NetworkNumber,
)
if err != nil {
return &NetInterface{}, err
@@ -112,7 +112,7 @@ func newNetworkInterface(cfg NetDevConfig, a *Ip4AddressAllocator) (*NetInterfac
peer := cfg["peer"].(NetDevConfig)
- if newInterface.peer, err = newNetworkInterface(peer, a); err != nil {
+ if newInterface.Peer, err = newNetworkInterface(peer, a); err != nil {
return &NetInterface{}, err
}
@@ -128,8 +128,8 @@ func (n *NetInterface) configureUpState() error {
}
func (n *NetInterface) configureNetworkNamespace() error {
- if n.networkNamespace != "" {
- err := linkSetNetns(n.name, n.networkNamespace)
+ if n.NetworkNamespace != "" {
+ err := linkSetNetns(n.name, n.NetworkNamespace)
if err != nil {
return err
}
@@ -138,11 +138,11 @@ func (n *NetInterface) configureNetworkNamespace() error {
}
func (n *NetInterface) configureAddress() error {
- if n.ip4Address != "" {
+ if n.Ip4Address != "" {
if err := addAddress(
n.Name(),
- n.ip4Address,
- n.networkNamespace,
+ n.Ip4Address,
+ n.NetworkNamespace,
); err != nil {
return err
}
@@ -170,16 +170,16 @@ func (n *NetInterface) configure() error {
return err
}
- if n.peer != nil && n.peer.name != "" {
- if err := n.peer.configureUpState(); err != nil {
+ if n.Peer != nil && n.Peer.name != "" {
+ if err := n.Peer.configureUpState(); err != nil {
return err
}
- if err := n.peer.configureNetworkNamespace(); err != nil {
+ if err := n.Peer.configureNetworkNamespace(); err != nil {
return err
}
- if err := n.peer.configureAddress(); err != nil {
+ if err := n.Peer.configureAddress(); err != nil {
return err
}
}
@@ -199,19 +199,19 @@ func (n *NetInterface) Type() string {
return n.category
}
-func (n *NetInterface) addressWithPrefix() AddressWithPrefix {
- address, _ := ip_types.ParseAddressWithPrefix(n.ip4Address)
+func (n *NetInterface) AddressWithPrefix() AddressWithPrefix {
+ address, _ := ip_types.ParseAddressWithPrefix(n.Ip4Address)
return address
}
-func (n *NetInterface) ip4AddressWithPrefix() IP4AddressWithPrefix {
- ip4Prefix, _ := ip_types.ParseIP4Prefix(n.ip4Address)
- ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix)
- return ip4AddressWithPrefix
+func (n *NetInterface) Ip4AddressWithPrefix() IP4AddressWithPrefix {
+ ip4Prefix, _ := ip_types.ParseIP4Prefix(n.Ip4Address)
+ Ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix)
+ return Ip4AddressWithPrefix
}
-func (n *NetInterface) ip4AddressString() string {
- return strings.Split(n.ip4Address, "/")[0]
+func (n *NetInterface) Ip4AddressString() string {
+ return strings.Split(n.Ip4Address, "/")[0]
}
func (b *NetConfigBase) Name() string {
@@ -242,22 +242,22 @@ func newBridge(cfg NetDevConfig) (NetworkBridge, error) {
bridge.name = cfg["name"].(string)
bridge.category = Bridge
for _, v := range cfg["interfaces"].([]interface{}) {
- bridge.interfaces = append(bridge.interfaces, v.(string))
+ bridge.Interfaces = append(bridge.Interfaces, v.(string))
}
- bridge.networkNamespace = ""
+ bridge.NetworkNamespace = ""
if netns, ok := cfg["netns"]; ok {
- bridge.networkNamespace = netns.(string)
+ bridge.NetworkNamespace = netns.(string)
}
return bridge, nil
}
func (b *NetworkBridge) configure() error {
- return addBridge(b.name, b.interfaces, b.networkNamespace)
+ return addBridge(b.name, b.Interfaces, b.NetworkNamespace)
}
func (b *NetworkBridge) unconfigure() {
- delBridge(b.name, b.networkNamespace)
+ delBridge(b.name, b.NetworkNamespace)
}
func delBridge(brName, ns string) error {
diff --git a/extras/hs-test/infra/suite_cpu_pinning.go b/extras/hs-test/infra/suite_cpu_pinning.go
new file mode 100644
index 00000000000..e829efa950b
--- /dev/null
+++ b/extras/hs-test/infra/suite_cpu_pinning.go
@@ -0,0 +1,112 @@
+package hst
+
+import (
+ "fmt"
+ . "github.com/onsi/ginkgo/v2"
+ "reflect"
+ "runtime"
+ "strings"
+)
+
+var cpuPinningTests = map[string][]func(s *CpuPinningSuite){}
+var cpuPinningSoloTests = map[string][]func(s *CpuPinningSuite){}
+
+type CpuPinningSuite struct {
+ HstSuite
+ previousMaxContainerCount int
+}
+
+func RegisterCpuPinningTests(tests ...func(s *CpuPinningSuite)) {
+ cpuPinningTests[getTestFilename()] = tests
+}
+
+func RegisterCpuPinningSoloTests(tests ...func(s *CpuPinningSuite)) {
+ cpuPinningSoloTests[getTestFilename()] = tests
+}
+
+func (s *CpuPinningSuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.LoadNetworkTopology("tap")
+ s.LoadContainerTopology("singleCpuPinning")
+}
+
+func (s *CpuPinningSuite) SetupTest() {
+ // Skip if we cannot allocate 3 CPUs for test container
+ s.previousMaxContainerCount = s.CpuAllocator.maxContainerCount
+ s.CpuCount = 3
+ s.CpuAllocator.maxContainerCount = 1
+ s.SkipIfNotEnoughAvailableCpus()
+
+ s.HstSuite.SetupTest()
+ container := s.GetContainerByName(SingleTopoContainerVpp)
+ vpp, err := container.newVppInstance(container.AllocatedCpus)
+ s.AssertNotNil(vpp, fmt.Sprint(err))
+}
+
+func (s *CpuPinningSuite) TearDownTest() {
+ // reset vars
+ s.CpuCount = *NConfiguredCpus
+ s.CpuAllocator.maxContainerCount = s.previousMaxContainerCount
+ s.HstSuite.TearDownTest()
+
+}
+
+var _ = Describe("CpuPinningSuite", Ordered, ContinueOnFailure, func() {
+ var s CpuPinningSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range cpuPinningTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("CpuPinningSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s CpuPinningSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range cpuPinningSoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_envoy_proxy.go b/extras/hs-test/infra/suite_envoy_proxy.go
new file mode 100644
index 00000000000..80715214cac
--- /dev/null
+++ b/extras/hs-test/infra/suite_envoy_proxy.go
@@ -0,0 +1,212 @@
+// Suite for Envoy proxy testing
+//
+// The topology consists of 4 containers: curl (client), VPP (session layer), Envoy (proxy), nginx (target HTTP server).
+// VPP has 2 tap interfaces configured, one for client network and second for server/target network.
+
+package hst
+
+import (
+ "fmt"
+ . "github.com/onsi/ginkgo/v2"
+ "reflect"
+ "runtime"
+ "strings"
+)
+
+const (
+ VppContainerName = "vpp"
+ EnvoyProxyContainerName = "envoy-vcl"
+)
+
+type EnvoyProxySuite struct {
+ HstSuite
+ nginxPort uint16
+ proxyPort uint16
+ maxTimeout int
+}
+
+var envoyProxyTests = map[string][]func(s *EnvoyProxySuite){}
+var envoyProxySoloTests = map[string][]func(s *EnvoyProxySuite){}
+
+func RegisterEnvoyProxyTests(tests ...func(s *EnvoyProxySuite)) {
+ envoyProxyTests[getTestFilename()] = tests
+}
+
+func RegisterEnvoyProxySoloTests(tests ...func(s *EnvoyProxySuite)) {
+ envoyProxySoloTests[getTestFilename()] = tests
+}
+
+func (s *EnvoyProxySuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.LoadNetworkTopology("2taps")
+ s.LoadContainerTopology("envoyProxy")
+
+ if *IsVppDebug {
+ s.maxTimeout = 600
+ } else {
+ s.maxTimeout = 60
+ }
+}
+
+func (s *EnvoyProxySuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // VPP
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api").
+ Append("evt_qs_memfd_seg").
+ Append("event-queue-length 100000")
+
+ vppContainer := s.GetContainerByName(VppContainerName)
+ vpp, err := vppContainer.newVppInstance(vppContainer.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(vpp, fmt.Sprint(err))
+ s.AssertNil(vpp.Start())
+ clientInterface := s.GetInterfaceByName(ClientTapInterfaceName)
+ s.AssertNil(vpp.createTap(clientInterface, 1))
+ serverInterface := s.GetInterfaceByName(ServerTapInterfaceName)
+ s.AssertNil(vpp.createTap(serverInterface, 2))
+ vppContainer.Exec("chmod 777 -R %s", vppContainer.GetContainerWorkDir())
+
+ // nginx HTTP server
+ nginxContainer := s.GetTransientContainerByName(NginxServerContainerName)
+ s.AssertNil(nginxContainer.Create())
+ s.nginxPort = 80
+ nginxSettings := struct {
+ LogPrefix string
+ Address string
+ Port uint16
+ Timeout int
+ }{
+ LogPrefix: nginxContainer.Name,
+ Address: serverInterface.Ip4AddressString(),
+ Port: s.nginxPort,
+ Timeout: s.maxTimeout,
+ }
+ nginxContainer.CreateConfig(
+ "/nginx.conf",
+ "./resources/nginx/nginx_server.conf",
+ nginxSettings,
+ )
+ s.AssertNil(nginxContainer.Start())
+
+ // Envoy
+ envoyContainer := s.GetContainerByName(EnvoyProxyContainerName)
+ s.AssertNil(envoyContainer.Create())
+ s.proxyPort = 8080
+ envoySettings := struct {
+ LogPrefix string
+ ServerAddress string
+ ServerPort uint16
+ ProxyPort uint16
+ }{
+ LogPrefix: envoyContainer.Name,
+ ServerAddress: serverInterface.Ip4AddressString(),
+ ServerPort: s.nginxPort,
+ ProxyPort: s.proxyPort,
+ }
+ envoyContainer.CreateConfig(
+ "/etc/envoy/envoy.yaml",
+ "resources/envoy/proxy.yaml",
+ envoySettings,
+ )
+ s.AssertNil(envoyContainer.Start())
+
+ // Add Ipv4 ARP entry for nginx HTTP server, otherwise first request fail (HTTP error 503)
+ arp := fmt.Sprintf("set ip neighbor %s %s %s",
+ serverInterface.Peer.Name(),
+ serverInterface.Ip4AddressString(),
+ serverInterface.HwAddress)
+ vppContainer.VppInstance.Vppctl(arp)
+}
+
+func (s *EnvoyProxySuite) TearDownTest() {
+ if CurrentSpecReport().Failed() {
+ s.CollectNginxLogs(NginxServerContainerName)
+ s.CollectEnvoyLogs(EnvoyProxyContainerName)
+ }
+ s.HstSuite.TearDownTest()
+}
+
+func (s *EnvoyProxySuite) ProxyPort() uint16 {
+ return s.proxyPort
+}
+
+func (s *EnvoyProxySuite) ProxyAddr() string {
+ return s.GetInterfaceByName(ClientTapInterfaceName).Peer.Ip4AddressString()
+}
+
+func (s *EnvoyProxySuite) CurlDownloadResource(uri string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_download --max-time %d --insecure --noproxy '*' --remote-name --output-dir /tmp %s", s.maxTimeout, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "GET response code: 200")
+ s.AssertNotContains(log, "bytes remaining to read")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+func (s *EnvoyProxySuite) CurlUploadResource(uri, file string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_upload --max-time %d --insecure --noproxy '*' -T %s %s", s.maxTimeout, file, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "PUT response code: 201")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+var _ = Describe("EnvoyProxySuite", Ordered, ContinueOnFailure, func() {
+ var s EnvoyProxySuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range envoyProxyTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("EnvoyProxySuiteSolo", Ordered, ContinueOnFailure, func() {
+ var s EnvoyProxySuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range envoyProxySoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_iperf_linux.go b/extras/hs-test/infra/suite_iperf_linux.go
new file mode 100644
index 00000000000..728429b505f
--- /dev/null
+++ b/extras/hs-test/infra/suite_iperf_linux.go
@@ -0,0 +1,96 @@
+package hst
+
+import (
+ "reflect"
+ "runtime"
+ "strings"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+type IperfSuite struct {
+ HstSuite
+}
+
+const (
+ ServerIperfContainerName string = "server"
+ ServerIperfInterfaceName string = "hstsrv"
+ ClientIperfContainerName string = "client"
+ ClientIperfInterfaceName string = "hstcln"
+)
+
+var iperfTests = map[string][]func(s *IperfSuite){}
+var iperfSoloTests = map[string][]func(s *IperfSuite){}
+
+func RegisterIperfTests(tests ...func(s *IperfSuite)) {
+ iperfTests[getTestFilename()] = tests
+}
+func RegisterIperfSoloTests(tests ...func(s *IperfSuite)) {
+ iperfSoloTests[getTestFilename()] = tests
+}
+
+func (s *IperfSuite) SetupSuite() {
+ time.Sleep(1 * time.Second)
+ s.HstSuite.SetupSuite()
+ s.ConfigureNetworkTopology("2taps")
+ s.LoadContainerTopology("2containers")
+}
+
+var _ = Describe("IperfSuite", Ordered, ContinueOnFailure, func() {
+ var s IperfSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range iperfTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("IperfSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s IperfSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range iperfSoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_ldp.go b/extras/hs-test/infra/suite_ldp.go
new file mode 100644
index 00000000000..15b45f710ef
--- /dev/null
+++ b/extras/hs-test/infra/suite_ldp.go
@@ -0,0 +1,203 @@
+package hst
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+// These correspond to names used in yaml config
+const (
+ ServerLdpInterfaceName = "srv"
+ ClientLdpInterfaceName = "cln"
+)
+
+var ldpTests = map[string][]func(s *LdpSuite){}
+var ldpSoloTests = map[string][]func(s *LdpSuite){}
+
+type LdpSuite struct {
+ HstSuite
+}
+
+func RegisterLdpTests(tests ...func(s *LdpSuite)) {
+ ldpTests[getTestFilename()] = tests
+}
+func RegisterSoloLdpTests(tests ...func(s *LdpSuite)) {
+ ldpSoloTests[getTestFilename()] = tests
+}
+
+func (s *LdpSuite) SetupSuite() {
+ time.Sleep(1 * time.Second)
+ s.HstSuite.SetupSuite()
+ s.ConfigureNetworkTopology("2peerVeth")
+ s.LoadContainerTopology("2peerVethLdp")
+}
+
+func (s *LdpSuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // Setup test conditions
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api")
+
+ if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
+ sessionConfig.Append("use-private-rx-mqs").Close()
+ s.Log("**********************INTERRUPT MODE**********************")
+ } else {
+ sessionConfig.Close()
+ }
+
+ // ... For server
+ serverContainer := s.GetContainerByName("server-vpp")
+
+ serverVpp, err := serverContainer.newVppInstance(serverContainer.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(serverVpp, fmt.Sprint(err))
+
+ s.SetupServerVpp()
+
+ // ... For client
+ clientContainer := s.GetContainerByName("client-vpp")
+
+ clientVpp, err := clientContainer.newVppInstance(clientContainer.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(clientVpp, fmt.Sprint(err))
+
+ s.setupClientVpp()
+
+ serverContainer.AddEnvVar("VCL_CONFIG", serverContainer.GetContainerWorkDir()+"/vcl_srv.conf")
+ clientContainer.AddEnvVar("VCL_CONFIG", clientContainer.GetContainerWorkDir()+"/vcl_cln.conf")
+
+ for _, container := range s.StartedContainers {
+ container.AddEnvVar("LD_PRELOAD", "/usr/lib/libvcl_ldpreload.so")
+ container.AddEnvVar("LDP_DEBUG", "0")
+ container.AddEnvVar("VCL_DEBUG", "0")
+ }
+}
+
+func (s *LdpSuite) TearDownTest() {
+ for _, container := range s.StartedContainers {
+ delete(container.EnvVars, "LD_PRELOAD")
+ delete(container.EnvVars, "VCL_CONFIG")
+ }
+ s.HstSuite.TearDownTest()
+
+}
+
+func (s *LdpSuite) SetupServerVpp() {
+ var srvVclConf Stanza
+ serverContainer := s.GetContainerByName("server-vpp")
+ serverVclFileName := serverContainer.GetHostWorkDir() + "/vcl_srv.conf"
+ serverVpp := serverContainer.VppInstance
+ s.AssertNil(serverVpp.Start())
+
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ idx, err := serverVpp.createAfPacket(serverVeth)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+
+ serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
+ serverContainer.GetContainerWorkDir())
+ err = srvVclConf.
+ NewStanza("vcl").
+ Append("rx-fifo-size 4000000").
+ Append("tx-fifo-size 4000000").
+ Append("app-scope-local").
+ Append("app-scope-global").
+ Append("use-mq-eventfd").
+ Append(serverAppSocketApi).Close().
+ SaveToFile(serverVclFileName)
+ s.AssertNil(err, fmt.Sprint(err))
+}
+
+func (s *LdpSuite) setupClientVpp() {
+ var clnVclConf Stanza
+ clientContainer := s.GetContainerByName("client-vpp")
+ clientVclFileName := clientContainer.GetHostWorkDir() + "/vcl_cln.conf"
+ clientVpp := clientContainer.VppInstance
+ s.AssertNil(clientVpp.Start())
+
+ clientVeth := s.GetInterfaceByName(ClientInterfaceName)
+ idx, err := clientVpp.createAfPacket(clientVeth)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+
+ clientAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
+ clientContainer.GetContainerWorkDir())
+ err = clnVclConf.
+ NewStanza("vcl").
+ Append("rx-fifo-size 4000000").
+ Append("tx-fifo-size 4000000").
+ Append("app-scope-local").
+ Append("app-scope-global").
+ Append("use-mq-eventfd").
+ Append(clientAppSocketApi).Close().
+ SaveToFile(clientVclFileName)
+ s.AssertNil(err, fmt.Sprint(err))
+}
+
+var _ = Describe("LdpSuite", Ordered, ContinueOnFailure, func() {
+ var s LdpSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range ldpTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("LdpSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s LdpSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range ldpSoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_nginx_proxy.go b/extras/hs-test/infra/suite_nginx_proxy.go
new file mode 100644
index 00000000000..75215cfc78d
--- /dev/null
+++ b/extras/hs-test/infra/suite_nginx_proxy.go
@@ -0,0 +1,191 @@
+package hst
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+// These correspond to names used in yaml config
+const (
+ NginxProxyContainerName = "nginx-proxy"
+ NginxServerContainerName = "nginx-server"
+ MirroringClientInterfaceName = "hstcln"
+ MirroringServerInterfaceName = "hstsrv"
+)
+
+var nginxProxyTests = map[string][]func(s *NginxProxySuite){}
+var nginxProxySoloTests = map[string][]func(s *NginxProxySuite){}
+
+type NginxProxySuite struct {
+ HstSuite
+ proxyPort uint16
+ maxTimeout int
+}
+
+func RegisterNginxProxyTests(tests ...func(s *NginxProxySuite)) {
+ nginxProxyTests[getTestFilename()] = tests
+}
+func RegisterNginxProxySoloTests(tests ...func(s *NginxProxySuite)) {
+ nginxProxySoloTests[getTestFilename()] = tests
+}
+
+func (s *NginxProxySuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.LoadNetworkTopology("2taps")
+ s.LoadContainerTopology("nginxProxy")
+
+ if *IsVppDebug {
+ s.maxTimeout = 600
+ } else {
+ s.maxTimeout = 60
+ }
+}
+
+func (s *NginxProxySuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // VPP
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api")
+
+ vppContainer := s.GetContainerByName(VppContainerName)
+ vpp, err := vppContainer.newVppInstance(vppContainer.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(vpp, fmt.Sprint(err))
+ s.AssertNil(vpp.Start())
+ clientInterface := s.GetInterfaceByName(MirroringClientInterfaceName)
+ s.AssertNil(vpp.createTap(clientInterface, 1))
+ serverInterface := s.GetInterfaceByName(MirroringServerInterfaceName)
+ s.AssertNil(vpp.createTap(serverInterface, 2))
+
+ // nginx proxy
+ nginxProxyContainer := s.GetTransientContainerByName(NginxProxyContainerName)
+ s.AssertNil(nginxProxyContainer.Create())
+ s.proxyPort = 80
+ values := struct {
+ LogPrefix string
+ Proxy string
+ Server string
+ Port uint16
+ }{
+ LogPrefix: nginxProxyContainer.Name,
+ Proxy: clientInterface.Peer.Ip4AddressString(),
+ Server: serverInterface.Ip4AddressString(),
+ Port: s.proxyPort,
+ }
+ nginxProxyContainer.CreateConfig(
+ "/nginx.conf",
+ "./resources/nginx/nginx_proxy_mirroring.conf",
+ values,
+ )
+ s.AssertNil(nginxProxyContainer.Start())
+
+ // nginx HTTP server
+ nginxServerContainer := s.GetTransientContainerByName(NginxServerContainerName)
+ s.AssertNil(nginxServerContainer.Create())
+ nginxSettings := struct {
+ LogPrefix string
+ Address string
+ Timeout int
+ }{
+ LogPrefix: nginxServerContainer.Name,
+ Address: serverInterface.Ip4AddressString(),
+ Timeout: s.maxTimeout,
+ }
+ nginxServerContainer.CreateConfig(
+ "/nginx.conf",
+ "./resources/nginx/nginx_server_mirroring.conf",
+ nginxSettings,
+ )
+ s.AssertNil(nginxServerContainer.Start())
+
+ vpp.WaitForApp("nginx-", 5)
+}
+
+func (s *NginxProxySuite) TearDownTest() {
+ if CurrentSpecReport().Failed() {
+ s.CollectNginxLogs(NginxServerContainerName)
+ s.CollectNginxLogs(NginxProxyContainerName)
+ }
+ s.HstSuite.TearDownTest()
+}
+
+func (s *NginxProxySuite) ProxyPort() uint16 {
+ return s.proxyPort
+}
+
+func (s *NginxProxySuite) ProxyAddr() string {
+ return s.GetInterfaceByName(MirroringClientInterfaceName).Peer.Ip4AddressString()
+}
+
+func (s *NginxProxySuite) CurlDownloadResource(uri string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_download --max-time %d --insecure --noproxy '*' --remote-name --output-dir /tmp %s", s.maxTimeout, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "GET response code: 200")
+ s.AssertNotContains(log, "bytes remaining to read")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+var _ = Describe("NginxProxySuite", Ordered, ContinueOnFailure, func() {
+ var s NginxProxySuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range nginxProxyTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("NginxProxySuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s NginxProxySuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range nginxProxySoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_no_topo.go b/extras/hs-test/infra/suite_no_topo.go
new file mode 100644
index 00000000000..9b4998a77a1
--- /dev/null
+++ b/extras/hs-test/infra/suite_no_topo.go
@@ -0,0 +1,131 @@
+package hst
+
+import (
+ "reflect"
+ "runtime"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+const (
+ SingleTopoContainerVpp = "vpp"
+ SingleTopoContainerNginx = "nginx"
+ TapInterfaceName = "htaphost"
+)
+
+var noTopoTests = map[string][]func(s *NoTopoSuite){}
+var noTopoSoloTests = map[string][]func(s *NoTopoSuite){}
+
+type NoTopoSuite struct {
+ HstSuite
+}
+
+func RegisterNoTopoTests(tests ...func(s *NoTopoSuite)) {
+ noTopoTests[getTestFilename()] = tests
+}
+func RegisterNoTopoSoloTests(tests ...func(s *NoTopoSuite)) {
+ noTopoSoloTests[getTestFilename()] = tests
+}
+
+func (s *NoTopoSuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.LoadNetworkTopology("tap")
+ s.LoadContainerTopology("single")
+}
+
+func (s *NoTopoSuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // Setup test conditions
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api")
+
+ if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
+ sessionConfig.Append("use-private-rx-mqs").Close()
+ s.Log("**********************INTERRUPT MODE**********************")
+ } else {
+ sessionConfig.Close()
+ }
+
+ container := s.GetContainerByName(SingleTopoContainerVpp)
+ vpp, _ := container.newVppInstance(container.AllocatedCpus, sessionConfig)
+ s.AssertNil(vpp.Start())
+
+ tapInterface := s.GetInterfaceByName(TapInterfaceName)
+
+ s.AssertNil(vpp.createTap(tapInterface), "failed to create tap interface")
+}
+
+func (s *NoTopoSuite) VppAddr() string {
+ return s.GetInterfaceByName(TapInterfaceName).Peer.Ip4AddressString()
+}
+
+func (s *NoTopoSuite) VppIfName() string {
+ return s.GetInterfaceByName(TapInterfaceName).Peer.Name()
+}
+
+func (s *NoTopoSuite) HostAddr() string {
+ return s.GetInterfaceByName(TapInterfaceName).Ip4AddressString()
+}
+
+var _ = Describe("NoTopoSuite", Ordered, ContinueOnFailure, func() {
+ var s NoTopoSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range noTopoTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("NoTopoSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s NoTopoSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range noTopoSoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_ns.go b/extras/hs-test/infra/suite_ns.go
new file mode 100644
index 00000000000..601ec22a8a9
--- /dev/null
+++ b/extras/hs-test/infra/suite_ns.go
@@ -0,0 +1,127 @@
+package hst
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+// These correspond to names used in yaml config
+const (
+ ClientInterface = "hclnvpp"
+ ServerInterface = "hsrvvpp"
+)
+
+var nsTests = map[string][]func(s *NsSuite){}
+var nsSoloTests = map[string][]func(s *NsSuite){}
+
+type NsSuite struct {
+ HstSuite
+}
+
+func RegisterNsTests(tests ...func(s *NsSuite)) {
+ nsTests[getTestFilename()] = tests
+}
+func RegisterNsSoloTests(tests ...func(s *NsSuite)) {
+ nsSoloTests[getTestFilename()] = tests
+}
+
+func (s *NsSuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.ConfigureNetworkTopology("ns")
+ s.LoadContainerTopology("ns")
+}
+
+func (s *NsSuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // Setup test conditions
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api").
+ Append("evt_qs_memfd_seg").
+ Append("event-queue-length 100000")
+
+ if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
+ sessionConfig.Append("use-private-rx-mqs").Close()
+ } else {
+ sessionConfig.Close()
+ }
+
+ container := s.GetContainerByName("vpp")
+ vpp, _ := container.newVppInstance(container.AllocatedCpus, sessionConfig)
+ s.AssertNil(vpp.Start())
+
+ idx, err := vpp.createAfPacket(s.GetInterfaceByName(ServerInterface))
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+
+ idx, err = vpp.createAfPacket(s.GetInterfaceByName(ClientInterface))
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+
+ container.Exec("chmod 777 -R %s", container.GetContainerWorkDir())
+}
+
+var _ = Describe("NsSuite", Ordered, ContinueOnFailure, func() {
+ var s NsSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range nsTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("NsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s NsSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range nsSoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_veth.go b/extras/hs-test/infra/suite_veth.go
new file mode 100644
index 00000000000..f7b1c3da7d8
--- /dev/null
+++ b/extras/hs-test/infra/suite_veth.go
@@ -0,0 +1,153 @@
+package hst
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+// These correspond to names used in yaml config
+const (
+ ServerInterfaceName = "srv"
+ ClientInterfaceName = "cln"
+)
+
+var vethTests = map[string][]func(s *VethsSuite){}
+var vethSoloTests = map[string][]func(s *VethsSuite){}
+
+type VethsSuite struct {
+ HstSuite
+}
+
+func RegisterVethTests(tests ...func(s *VethsSuite)) {
+ vethTests[getTestFilename()] = tests
+}
+func RegisterSoloVethTests(tests ...func(s *VethsSuite)) {
+ vethSoloTests[getTestFilename()] = tests
+}
+
+func (s *VethsSuite) SetupSuite() {
+ time.Sleep(1 * time.Second)
+ s.HstSuite.SetupSuite()
+ s.ConfigureNetworkTopology("2peerVeth")
+ s.LoadContainerTopology("2peerVeth")
+}
+
+func (s *VethsSuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // Setup test conditions
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api")
+
+ if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
+ sessionConfig.Append("use-private-rx-mqs").Close()
+ s.Log("**********************INTERRUPT MODE**********************")
+ } else {
+ sessionConfig.Close()
+ }
+
+ // ... For server
+ serverContainer := s.GetContainerByName("server-vpp")
+
+ serverVpp, err := serverContainer.newVppInstance(serverContainer.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(serverVpp, fmt.Sprint(err))
+
+ s.SetupServerVpp()
+
+ // ... For client
+ clientContainer := s.GetContainerByName("client-vpp")
+
+ clientVpp, err := clientContainer.newVppInstance(clientContainer.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(clientVpp, fmt.Sprint(err))
+
+ s.setupClientVpp()
+}
+
+func (s *VethsSuite) SetupServerVpp() {
+ serverVpp := s.GetContainerByName("server-vpp").VppInstance
+ s.AssertNil(serverVpp.Start())
+
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ idx, err := serverVpp.createAfPacket(serverVeth)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+}
+
+func (s *VethsSuite) setupClientVpp() {
+ clientVpp := s.GetContainerByName("client-vpp").VppInstance
+ s.AssertNil(clientVpp.Start())
+
+ clientVeth := s.GetInterfaceByName(ClientInterfaceName)
+ idx, err := clientVpp.createAfPacket(clientVeth)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+}
+
+var _ = Describe("VethsSuite", Ordered, ContinueOnFailure, func() {
+ var s VethsSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range vethTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("VethsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s VethsSuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range vethSoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/infra/suite_vpp_proxy.go b/extras/hs-test/infra/suite_vpp_proxy.go
new file mode 100644
index 00000000000..21e81e0581e
--- /dev/null
+++ b/extras/hs-test/infra/suite_vpp_proxy.go
@@ -0,0 +1,211 @@
+// Suite for VPP proxy testing
+//
+// The topology consists of 3 containers: curl (client), VPP (proxy), nginx (target HTTP server).
+// VPP has 2 tap interfaces configured, one for client network and second for server/target network.
+
+package hst
+
+import (
+ "fmt"
+ . "github.com/onsi/ginkgo/v2"
+ "reflect"
+ "runtime"
+ "strings"
+)
+
+// These correspond to names used in yaml config
+const (
+ VppProxyContainerName = "vpp-proxy"
+ ClientTapInterfaceName = "hstcln"
+ ServerTapInterfaceName = "hstsrv"
+ CurlContainerTestFile = "/tmp/testFile"
+)
+
+type VppProxySuite struct {
+ HstSuite
+ nginxPort uint16
+ maxTimeout int
+}
+
+var vppProxyTests = map[string][]func(s *VppProxySuite){}
+var vppProxySoloTests = map[string][]func(s *VppProxySuite){}
+
+func RegisterVppProxyTests(tests ...func(s *VppProxySuite)) {
+ vppProxyTests[getTestFilename()] = tests
+}
+
+func RegisterVppProxySoloTests(tests ...func(s *VppProxySuite)) {
+ vppProxySoloTests[getTestFilename()] = tests
+}
+
+func (s *VppProxySuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.LoadNetworkTopology("2taps")
+ s.LoadContainerTopology("vppProxy")
+
+ if *IsVppDebug {
+ s.maxTimeout = 600
+ } else {
+ s.maxTimeout = 60
+ }
+}
+
+func (s *VppProxySuite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // VPP HTTP connect-proxy
+ vppContainer := s.GetContainerByName(VppProxyContainerName)
+ vpp, err := vppContainer.newVppInstance(vppContainer.AllocatedCpus)
+ s.AssertNotNil(vpp, fmt.Sprint(err))
+ s.AssertNil(vpp.Start())
+ clientInterface := s.GetInterfaceByName(ClientTapInterfaceName)
+ s.AssertNil(vpp.createTap(clientInterface, 1))
+ serverInterface := s.GetInterfaceByName(ServerTapInterfaceName)
+ s.AssertNil(vpp.createTap(serverInterface, 2))
+
+ // nginx HTTP server
+ nginxContainer := s.GetTransientContainerByName(NginxServerContainerName)
+ s.AssertNil(nginxContainer.Create())
+ s.nginxPort = 80
+ nginxSettings := struct {
+ LogPrefix string
+ Address string
+ Port uint16
+ Timeout int
+ }{
+ LogPrefix: nginxContainer.Name,
+ Address: serverInterface.Ip4AddressString(),
+ Port: s.nginxPort,
+ Timeout: s.maxTimeout,
+ }
+ nginxContainer.CreateConfig(
+ "/nginx.conf",
+ "./resources/nginx/nginx_server.conf",
+ nginxSettings,
+ )
+ s.AssertNil(nginxContainer.Start())
+}
+
+func (s *VppProxySuite) TearDownTest() {
+ vpp := s.GetContainerByName(VppProxyContainerName).VppInstance
+ if CurrentSpecReport().Failed() {
+ s.Log(vpp.Vppctl("show session verbose 2"))
+ s.Log(vpp.Vppctl("show error"))
+ s.CollectNginxLogs(NginxServerContainerName)
+ }
+ s.HstSuite.TearDownTest()
+}
+
+func (s *VppProxySuite) NginxPort() uint16 {
+ return s.nginxPort
+}
+
+func (s *VppProxySuite) NginxAddr() string {
+ return s.GetInterfaceByName(ServerTapInterfaceName).Ip4AddressString()
+}
+
+func (s *VppProxySuite) VppProxyAddr() string {
+ return s.GetInterfaceByName(ClientTapInterfaceName).Peer.Ip4AddressString()
+}
+
+func (s *VppProxySuite) CurlRequest(targetUri string) (string, string) {
+ args := fmt.Sprintf("--insecure --noproxy '*' %s", targetUri)
+ body, log := s.RunCurlContainer(args)
+ return body, log
+}
+
+func (s *VppProxySuite) CurlRequestViaTunnel(targetUri string, proxyUri string) (string, string) {
+ args := fmt.Sprintf("--max-time %d --insecure -p -x %s %s", s.maxTimeout, proxyUri, targetUri)
+ body, log := s.RunCurlContainer(args)
+ return body, log
+}
+
+func (s *VppProxySuite) CurlDownloadResource(uri string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_download --max-time %d --insecure --noproxy '*' --remote-name --output-dir /tmp %s", s.maxTimeout, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "GET response code: 200")
+ s.AssertNotContains(log, "bytes remaining to read")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+func (s *VppProxySuite) CurlUploadResource(uri, file string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_upload --max-time %d --insecure --noproxy '*' -T %s %s", s.maxTimeout, file, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "PUT response code: 201")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+func (s *VppProxySuite) CurlDownloadResourceViaTunnel(uri string, proxyUri string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_download_connect --max-time %d --insecure -p -x %s --remote-name --output-dir /tmp %s", s.maxTimeout, proxyUri, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "CONNECT response code: 200")
+ s.AssertContains(writeOut, "GET response code: 200")
+ s.AssertNotContains(log, "bytes remaining to read")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+func (s *VppProxySuite) CurlUploadResourceViaTunnel(uri, proxyUri, file string) {
+ args := fmt.Sprintf("-w @/tmp/write_out_upload_connect --max-time %d --insecure -p -x %s -T %s %s", s.maxTimeout, proxyUri, file, uri)
+ writeOut, log := s.RunCurlContainer(args)
+ s.AssertContains(writeOut, "CONNECT response code: 200")
+ s.AssertContains(writeOut, "PUT response code: 201")
+ s.AssertNotContains(log, "Operation timed out")
+}
+
+var _ = Describe("VppProxySuite", Ordered, ContinueOnFailure, func() {
+ var s VppProxySuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range vppProxyTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
+
+var _ = Describe("VppProxySuiteSolo", Ordered, ContinueOnFailure, func() {
+ var s VppProxySuite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range vppProxySoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(SuiteTimeout))
+ }
+ }
+})
diff --git a/extras/hs-test/topo.go b/extras/hs-test/infra/topo.go
index 6cb294511b3..f9c6528ba93 100644
--- a/extras/hs-test/topo.go
+++ b/extras/hs-test/infra/topo.go
@@ -1,4 +1,4 @@
-package main
+package hst
import (
"fmt"
diff --git a/extras/hs-test/infra/utils.go b/extras/hs-test/infra/utils.go
new file mode 100644
index 00000000000..25d8519cb8a
--- /dev/null
+++ b/extras/hs-test/infra/utils.go
@@ -0,0 +1,316 @@
+package hst
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/http/httputil"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+)
+
+const networkTopologyDir string = "topo-network/"
+const containerTopologyDir string = "topo-containers/"
+
+type Stanza struct {
+ content string
+ pad int
+}
+
+type ActionResult struct {
+ Err error
+ Desc string
+ ErrOutput string
+ StdOutput string
+}
+
+type JsonResult struct {
+ Code int
+ Desc string
+ ErrOutput string
+ StdOutput string
+}
+
+func AssertFileSize(f1, f2 string) error {
+ fi1, err := os.Stat(f1)
+ if err != nil {
+ return err
+ }
+
+ fi2, err1 := os.Stat(f2)
+ if err1 != nil {
+ return err1
+ }
+
+ if fi1.Size() != fi2.Size() {
+ return fmt.Errorf("file sizes differ (%d vs %d)", fi1.Size(), fi2.Size())
+ }
+ return nil
+}
+
+func (c *Stanza) NewStanza(name string) *Stanza {
+ c.Append("\n" + name + " {")
+ c.pad += 2
+ return c
+}
+
+func (c *Stanza) Append(name string) *Stanza {
+ c.content += strings.Repeat(" ", c.pad)
+ c.content += name + "\n"
+ return c
+}
+
+func (c *Stanza) Close() *Stanza {
+ c.content += "}\n"
+ c.pad -= 2
+ return c
+}
+
+func (s *Stanza) ToString() string {
+ return s.content
+}
+
+func (s *Stanza) SaveToFile(fileName string) error {
+ fo, err := os.Create(fileName)
+ if err != nil {
+ return err
+ }
+ defer fo.Close()
+
+ _, err = io.Copy(fo, strings.NewReader(s.content))
+ return err
+}
+
+// NewHttpClient creates [http.Client] with disabled proxy and redirects, it also sets timeout to 30seconds.
+func NewHttpClient() *http.Client {
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ transport.(*http.Transport).DisableKeepAlives = true
+ client := &http.Client{
+ Transport: transport,
+ Timeout: time.Second * 30,
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ }}
+ return client
+}
+
+func DumpHttpResp(resp *http.Response, body bool) string {
+ dump, err := httputil.DumpResponse(resp, body)
+ if err != nil {
+ return ""
+ }
+ return string(dump)
+}
+
+func TcpSendReceive(address, data string) (string, error) {
+ conn, err := net.DialTimeout("tcp", address, time.Second*30)
+ if err != nil {
+ return "", err
+ }
+ defer conn.Close()
+ err = conn.SetDeadline(time.Now().Add(time.Second * 30))
+ if err != nil {
+ return "", err
+ }
+ _, err = conn.Write([]byte(data))
+ if err != nil {
+ return "", err
+ }
+ reply := make([]byte, 1024)
+ _, err = conn.Read(reply)
+ if err != nil {
+ return "", err
+ }
+ return string(reply), nil
+}
+
+/*
+RunCurlContainer execute curl command with given args.
+Container with name "curl" must be available.
+Curl runs in verbose mode and progress meter switch off by default.
+*/
+func (s *HstSuite) RunCurlContainer(args string) (string, string) {
+ curlCont := s.GetContainerByName("curl")
+ cmd := fmt.Sprintf("curl -v -s %s", args)
+ s.Log(cmd)
+ curlCont.ExtraRunningArgs = cmd
+ curlCont.Run()
+ stdout, stderr := curlCont.GetOutput()
+ s.Log(stderr)
+ s.Log(stdout)
+ return stdout, stderr
+}
+
+/*
+CollectNginxLogs save access and error logs to the test execution directory.
+Nginx logging need to be set following way:
+
+ - error_log <default-work-dir>/{{.LogPrefix}}-error.log;
+ - access_log <default-work-dir>/{{.LogPrefix}}-access.log;
+
+where LogPrefix is set to nginxContainer.Name
+*/
+func (s *HstSuite) CollectNginxLogs(containerName string) {
+ nginxContainer := s.GetContainerByName(containerName)
+ targetDir := nginxContainer.Suite.getLogDirPath()
+ source := nginxContainer.GetHostWorkDir() + "/" + nginxContainer.Name + "-"
+ cmd := exec.Command("cp", "-t", targetDir, source+"error.log", source+"access.log")
+ s.Log(cmd.String())
+ err := cmd.Run()
+ if err != nil {
+ s.Log(fmt.Sprint(err))
+ }
+}
+
+/*
+CollectEnvoyLogs save access logs to the test execution directory.
+Envoy access log path need to be set following way:
+<default-work-dir>/{{.LogPrefix}}-access.log
+where LogPrefix is set to envoyContainer.Name
+*/
+func (s *HstSuite) CollectEnvoyLogs(containerName string) {
+ envoyContainer := s.GetContainerByName(containerName)
+ targetDir := envoyContainer.Suite.getLogDirPath()
+ source := envoyContainer.GetHostWorkDir() + "/" + envoyContainer.Name + "-"
+ cmd := exec.Command("cp", "-t", targetDir, source+"access.log")
+ s.Log(cmd.String())
+ err := cmd.Run()
+ if err != nil {
+ s.Log(fmt.Sprint(err))
+ }
+}
+
+func (s *HstSuite) StartIperfServerApp(running chan error, done chan struct{}, env []string) {
+ cmd := exec.Command("iperf3", "-4", "-s", "-p", s.GetPortFromPpid())
+ if env != nil {
+ cmd.Env = env
+ }
+ s.Log(cmd)
+ err := cmd.Start()
+ if err != nil {
+ msg := fmt.Errorf("failed to start iperf server: %v", err)
+ running <- msg
+ return
+ }
+ running <- nil
+ <-done
+ cmd.Process.Kill()
+}
+
+func (s *HstSuite) StartIperfClientApp(ipAddress string, env []string, clnCh chan error, clnRes chan string) {
+ defer func() {
+ clnCh <- nil
+ }()
+
+ nTries := 0
+
+ for {
+ cmd := exec.Command("iperf3", "-c", ipAddress, "-u", "-l", "1460", "-b", "10g", "-p", s.GetPortFromPpid())
+ if env != nil {
+ cmd.Env = env
+ }
+ s.Log(cmd)
+ o, err := cmd.CombinedOutput()
+ if err != nil {
+ if nTries > 5 {
+ clnRes <- ""
+ clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o)
+ return
+ }
+ time.Sleep(1 * time.Second)
+ nTries++
+ continue
+ } else {
+ clnRes <- fmt.Sprintf("Client output: %s", o)
+ }
+ break
+ }
+}
+
+func (s *HstSuite) StartHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) {
+ cmd := newCommand([]string{"./http_server", addressPort, s.Ppid, s.ProcessIndex}, netNs)
+ err := cmd.Start()
+ s.Log(cmd)
+ if err != nil {
+ s.Log("Failed to start http server: " + fmt.Sprint(err))
+ return
+ }
+ running <- struct{}{}
+ <-done
+ cmd.Process.Kill()
+}
+
+func (s *HstSuite) StartWget(finished chan error, server_ip, port, query, netNs string) {
+ defer func() {
+ finished <- errors.New("wget error")
+ }()
+
+ cmd := newCommand([]string{"wget", "--timeout=10", "--no-proxy", "--tries=5", "-O", "/dev/null", server_ip + ":" + port + "/" + query},
+ netNs)
+ s.Log(cmd)
+ o, err := cmd.CombinedOutput()
+ if err != nil {
+ finished <- fmt.Errorf("wget error: '%v\n\n%s'", err, o)
+ return
+ } else if !strings.Contains(string(o), "200 OK") {
+ finished <- fmt.Errorf("wget error: response not 200 OK")
+ return
+ }
+ finished <- nil
+}
+
+// Start a server app. 'processName' is used to check whether the app started correctly.
+func (s *HstSuite) StartServerApp(c *Container, processName string, cmd string,
+ running chan error, done chan struct{}) {
+
+ s.Log("starting server")
+ c.ExecServer(cmd)
+ cmd2 := exec.Command("docker", "exec", c.Name, "pidof", processName)
+ err := cmd2.Run()
+ if err != nil {
+ msg := fmt.Errorf("failed to start server app: %v", err)
+ running <- msg
+ <-done
+ return
+ }
+ running <- nil
+ <-done
+}
+
+func (s *HstSuite) StartClientApp(c *Container, cmd string,
+ clnCh chan error, clnRes chan string) {
+ defer func() {
+ close(clnCh)
+ close(clnRes)
+ }()
+
+ s.Log("starting client app, please wait")
+
+ nTries := 0
+ for {
+ // exec.Cmd can only be used once, which is why it's in the loop
+ cmd2 := exec.Command("/bin/sh", "-c", "docker exec "+c.getEnvVarsAsCliOption()+" "+
+ c.Name+" "+cmd)
+ s.Log(cmd2)
+ o, err := cmd2.CombinedOutput()
+ if err != nil {
+ s.Log(err)
+ if nTries > 5 {
+ clnRes <- ""
+ clnCh <- fmt.Errorf("failed to start client app '%s'", err)
+ s.AssertNil(err, fmt.Sprint(err))
+ break
+ }
+ time.Sleep(1 * time.Second)
+ nTries++
+ } else {
+ clnRes <- fmt.Sprintf("Client output: %s", o)
+ break
+ }
+ }
+}
diff --git a/extras/hs-test/infra/vppinstance.go b/extras/hs-test/infra/vppinstance.go
new file mode 100644
index 00000000000..b3ae995870f
--- /dev/null
+++ b/extras/hs-test/infra/vppinstance.go
@@ -0,0 +1,646 @@
+package hst
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "go.fd.io/govpp/binapi/ethernet_types"
+ "io"
+ "net"
+ "os"
+ "os/exec"
+ "os/signal"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/edwarnicke/exechelper"
+ . "github.com/onsi/ginkgo/v2"
+ "github.com/sirupsen/logrus"
+
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/api"
+ "go.fd.io/govpp/binapi/af_packet"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/session"
+ "go.fd.io/govpp/binapi/tapv2"
+ "go.fd.io/govpp/core"
+)
+
+const vppConfigTemplate = `unix {
+ nodaemon
+ log %[1]s%[4]s
+ full-coredump
+ coredump-size unlimited
+ cli-listen %[1]s%[2]s
+ runtime-dir %[1]s/var/run
+}
+
+api-trace {
+ on
+}
+
+socksvr {
+ socket-name %[1]s%[3]s
+}
+
+statseg {
+ socket-name %[1]s/var/run/vpp/stats.sock
+}
+
+plugins {
+ plugin default { disable }
+
+ plugin unittest_plugin.so { enable }
+ plugin quic_plugin.so { enable }
+ plugin af_packet_plugin.so { enable }
+ plugin hs_apps_plugin.so { enable }
+ plugin http_plugin.so { enable }
+ plugin http_static_plugin.so { enable }
+ plugin prom_plugin.so { enable }
+ plugin tlsopenssl_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin mactime_plugin.so { enable }
+}
+
+logging {
+ default-log-level debug
+ default-syslog-log-level debug
+}
+
+`
+
+const (
+ defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
+ defaultApiSocketFilePath = "/var/run/vpp/api.sock"
+ defaultLogFilePath = "/var/log/vpp/vpp.log"
+)
+
+type VppInstance struct {
+ Container *Container
+ AdditionalConfig []Stanza
+ Connection *core.Connection
+ ApiStream api.Stream
+ Cpus []int
+ CpuConfig VppCpuConfig
+}
+
+type VppCpuConfig struct {
+ PinMainCpu bool
+ PinWorkersCorelist bool
+ SkipCores int
+}
+
+type VppMemTrace struct {
+ Count int `json:"count"`
+ Size int `json:"bytes"`
+ Sample string `json:"sample"`
+ Traceback []string `json:"traceback"`
+}
+
+func (vpp *VppInstance) getSuite() *HstSuite {
+ return vpp.Container.Suite
+}
+
+func (vpp *VppInstance) getCliSocket() string {
+ return fmt.Sprintf("%s%s", vpp.Container.GetContainerWorkDir(), defaultCliSocketFilePath)
+}
+
+func (vpp *VppInstance) getRunDir() string {
+ return vpp.Container.GetContainerWorkDir() + "/var/run/vpp"
+}
+
+func (vpp *VppInstance) getLogDir() string {
+ return vpp.Container.GetContainerWorkDir() + "/var/log/vpp"
+}
+
+func (vpp *VppInstance) getEtcDir() string {
+ return vpp.Container.GetContainerWorkDir() + "/etc/vpp"
+}
+
+func (vpp *VppInstance) Start() error {
+ maxReconnectAttempts := 3
+ // Replace default logger in govpp with our own
+ govppLogger := logrus.New()
+ govppLogger.SetOutput(io.MultiWriter(vpp.getSuite().Logger.Writer(), GinkgoWriter))
+ core.SetLogger(govppLogger)
+ // Create folders
+ containerWorkDir := vpp.Container.GetContainerWorkDir()
+
+ vpp.Container.Exec("mkdir --mode=0700 -p " + vpp.getRunDir())
+ vpp.Container.Exec("mkdir --mode=0700 -p " + vpp.getLogDir())
+ vpp.Container.Exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
+
+ // Create startup.conf inside the container
+ configContent := fmt.Sprintf(
+ vppConfigTemplate,
+ containerWorkDir,
+ defaultCliSocketFilePath,
+ defaultApiSocketFilePath,
+ defaultLogFilePath,
+ )
+ configContent += vpp.generateVPPCpuConfig()
+ for _, c := range vpp.AdditionalConfig {
+ configContent += c.ToString()
+ }
+ startupFileName := vpp.getEtcDir() + "/startup.conf"
+ vpp.Container.CreateFile(startupFileName, configContent)
+
+ // create wrapper script for vppctl with proper CLI socket path
+ cliContent := "#!/usr/bin/bash\nvppctl -s " + vpp.getRunDir() + "/cli.sock"
+ vppcliFileName := "/usr/bin/vppcli"
+ vpp.Container.CreateFile(vppcliFileName, cliContent)
+ vpp.Container.Exec("chmod 0755 " + vppcliFileName)
+
+ vpp.getSuite().Log("starting vpp")
+ if *IsVppDebug {
+ // default = 3; VPP will timeout while debugging if there are not enough attempts
+ maxReconnectAttempts = 5000
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, syscall.SIGQUIT)
+ cont := make(chan bool, 1)
+ go func() {
+ <-sig
+ cont <- true
+ }()
+
+ vpp.Container.ExecServer("su -c \"vpp -c " + startupFileName + " &> /proc/1/fd/1\"")
+ fmt.Println("run following command in different terminal:")
+ fmt.Println("docker exec -it " + vpp.Container.Name + " gdb -ex \"attach $(docker exec " + vpp.Container.Name + " pidof vpp)\"")
+ fmt.Println("Afterwards press CTRL+\\ to continue")
+ <-cont
+ fmt.Println("continuing...")
+ } else {
+ // Start VPP
+ vpp.Container.ExecServer("su -c \"vpp -c " + startupFileName + " &> /proc/1/fd/1\"")
+ }
+
+ vpp.getSuite().Log("connecting to vpp")
+ // Connect to VPP and store the connection
+ sockAddress := vpp.Container.GetHostWorkDir() + defaultApiSocketFilePath
+ conn, connEv, err := govpp.AsyncConnect(
+ sockAddress,
+ maxReconnectAttempts,
+ core.DefaultReconnectInterval)
+ if err != nil {
+ vpp.getSuite().Log("async connect error: " + fmt.Sprint(err))
+ return err
+ }
+ vpp.Connection = conn
+
+ // ... wait for Connected event
+ e := <-connEv
+ if e.State != core.Connected {
+ vpp.getSuite().Log("connecting to VPP failed: " + fmt.Sprint(e.Error))
+ return e.Error
+ }
+
+ ch, err := conn.NewStream(
+ context.Background(),
+ core.WithRequestSize(50),
+ core.WithReplySize(50),
+ core.WithReplyTimeout(time.Second*5))
+ if err != nil {
+ vpp.getSuite().Log("creating stream failed: " + fmt.Sprint(err))
+ return err
+ }
+ vpp.ApiStream = ch
+
+ return nil
+}
+
+func (vpp *VppInstance) Vppctl(command string, arguments ...any) string {
+ vppCliCommand := fmt.Sprintf(command, arguments...)
+ containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
+ vpp.Container.Name, vpp.getCliSocket(), vppCliCommand)
+ vpp.getSuite().Log(containerExecCommand)
+ output, err := exechelper.CombinedOutput(containerExecCommand)
+ vpp.getSuite().AssertNil(err)
+
+ return string(output)
+}
+
+func (vpp *VppInstance) GetSessionStat(stat string) int {
+ o := vpp.Vppctl("show session stats")
+ vpp.getSuite().Log(o)
+ for _, line := range strings.Split(o, "\n") {
+ if strings.Contains(line, stat) {
+ tokens := strings.Split(strings.TrimSpace(line), " ")
+ val, err := strconv.Atoi(tokens[0])
+ if err != nil {
+ Fail("failed to parse stat value %s" + fmt.Sprint(err))
+ return 0
+ }
+ return val
+ }
+ }
+ return 0
+}
+
+func (vpp *VppInstance) WaitForApp(appName string, timeout int) {
+ vpp.getSuite().Log("waiting for app " + appName)
+ for i := 0; i < timeout; i++ {
+ o := vpp.Vppctl("show app")
+ if strings.Contains(o, appName) {
+ return
+ }
+ time.Sleep(1 * time.Second)
+ }
+ vpp.getSuite().AssertNil(1, "Timeout while waiting for app '%s'", appName)
+}
+
+func (vpp *VppInstance) createAfPacket(
+ veth *NetInterface,
+) (interface_types.InterfaceIndex, error) {
+ createReq := &af_packet.AfPacketCreateV3{
+ Mode: 1,
+ UseRandomHwAddr: true,
+ HostIfName: veth.Name(),
+ Flags: af_packet.AfPacketFlags(11),
+ }
+ if veth.HwAddress != (MacAddress{}) {
+ createReq.UseRandomHwAddr = false
+ createReq.HwAddr = veth.HwAddress
+ }
+
+ vpp.getSuite().Log("create af-packet interface " + veth.Name())
+ if err := vpp.ApiStream.SendMsg(createReq); err != nil {
+ vpp.getSuite().HstFail()
+ return 0, err
+ }
+ replymsg, err := vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return 0, err
+ }
+ reply := replymsg.(*af_packet.AfPacketCreateV3Reply)
+ err = api.RetvalToVPPApiError(reply.Retval)
+ if err != nil {
+ return 0, err
+ }
+
+ veth.Index = reply.SwIfIndex
+
+ // Set to up
+ upReq := &interfaces.SwInterfaceSetFlags{
+ SwIfIndex: veth.Index,
+ Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
+ }
+
+ vpp.getSuite().Log("set af-packet interface " + veth.Name() + " up")
+ if err := vpp.ApiStream.SendMsg(upReq); err != nil {
+ return 0, err
+ }
+ replymsg, err = vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return 0, err
+ }
+ reply2 := replymsg.(*interfaces.SwInterfaceSetFlagsReply)
+ if err = api.RetvalToVPPApiError(reply2.Retval); err != nil {
+ return 0, err
+ }
+
+ // Add address
+ if veth.AddressWithPrefix() == (AddressWithPrefix{}) {
+ var err error
+ var ip4Address string
+ if ip4Address, err = veth.Ip4AddrAllocator.NewIp4InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
+ veth.Ip4Address = ip4Address
+ } else {
+ return 0, err
+ }
+ }
+ addressReq := &interfaces.SwInterfaceAddDelAddress{
+ IsAdd: true,
+ SwIfIndex: veth.Index,
+ Prefix: veth.AddressWithPrefix(),
+ }
+
+ vpp.getSuite().Log("af-packet interface " + veth.Name() + " add address " + veth.Ip4Address)
+ if err := vpp.ApiStream.SendMsg(addressReq); err != nil {
+ return 0, err
+ }
+ replymsg, err = vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return 0, err
+ }
+ reply3 := replymsg.(*interfaces.SwInterfaceAddDelAddressReply)
+ err = api.RetvalToVPPApiError(reply3.Retval)
+ if err != nil {
+ return 0, err
+ }
+
+ return veth.Index, nil
+}
+
+func (vpp *VppInstance) addAppNamespace(
+ secret uint64,
+ ifx interface_types.InterfaceIndex,
+ namespaceId string,
+) error {
+ req := &session.AppNamespaceAddDelV4{
+ IsAdd: true,
+ Secret: secret,
+ SwIfIndex: ifx,
+ NamespaceID: namespaceId,
+ SockName: defaultApiSocketFilePath,
+ }
+
+ vpp.getSuite().Log("add app namespace " + namespaceId)
+ if err := vpp.ApiStream.SendMsg(req); err != nil {
+ return err
+ }
+ replymsg, err := vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return err
+ }
+ reply := replymsg.(*session.AppNamespaceAddDelV4Reply)
+ if err = api.RetvalToVPPApiError(reply.Retval); err != nil {
+ return err
+ }
+
+ sessionReq := &session.SessionEnableDisable{
+ IsEnable: true,
+ }
+
+ vpp.getSuite().Log("enable app namespace " + namespaceId)
+ if err := vpp.ApiStream.SendMsg(sessionReq); err != nil {
+ return err
+ }
+ replymsg, err = vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return err
+ }
+ reply2 := replymsg.(*session.SessionEnableDisableReply)
+ if err = api.RetvalToVPPApiError(reply2.Retval); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (vpp *VppInstance) createTap(
+ tap *NetInterface,
+ tapId ...uint32,
+) error {
+ var id uint32 = 1
+ if len(tapId) > 0 {
+ id = tapId[0]
+ }
+ createTapReq := &tapv2.TapCreateV3{
+ ID: id,
+ HostIfNameSet: true,
+ HostIfName: tap.Name(),
+ HostIP4PrefixSet: true,
+ HostIP4Prefix: tap.Ip4AddressWithPrefix(),
+ }
+
+ vpp.getSuite().Log("create tap interface " + tap.Name())
+ // Create tap interface
+ if err := vpp.ApiStream.SendMsg(createTapReq); err != nil {
+ return err
+ }
+ replymsg, err := vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return err
+ }
+ reply := replymsg.(*tapv2.TapCreateV3Reply)
+ if err = api.RetvalToVPPApiError(reply.Retval); err != nil {
+ return err
+ }
+ tap.Peer.Index = reply.SwIfIndex
+
+ // Get name and mac
+ if err := vpp.ApiStream.SendMsg(&interfaces.SwInterfaceDump{
+ SwIfIndex: reply.SwIfIndex,
+ }); err != nil {
+ return err
+ }
+ replymsg, err = vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return err
+ }
+ ifDetails := replymsg.(*interfaces.SwInterfaceDetails)
+ tap.Peer.name = ifDetails.InterfaceName
+ tap.Peer.HwAddress = ifDetails.L2Address
+
+ // Add address
+ addAddressReq := &interfaces.SwInterfaceAddDelAddress{
+ IsAdd: true,
+ SwIfIndex: reply.SwIfIndex,
+ Prefix: tap.Peer.AddressWithPrefix(),
+ }
+
+ vpp.getSuite().Log("tap interface " + tap.Name() + " add address " + tap.Peer.Ip4Address)
+ if err := vpp.ApiStream.SendMsg(addAddressReq); err != nil {
+ return err
+ }
+ replymsg, err = vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return err
+ }
+ reply2 := replymsg.(*interfaces.SwInterfaceAddDelAddressReply)
+ if err = api.RetvalToVPPApiError(reply2.Retval); err != nil {
+ return err
+ }
+
+ // Set interface to up
+ upReq := &interfaces.SwInterfaceSetFlags{
+ SwIfIndex: reply.SwIfIndex,
+ Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
+ }
+
+ vpp.getSuite().Log("set tap interface " + tap.Name() + " up")
+ if err := vpp.ApiStream.SendMsg(upReq); err != nil {
+ return err
+ }
+ replymsg, err = vpp.ApiStream.RecvMsg()
+ if err != nil {
+ return err
+ }
+ reply3 := replymsg.(*interfaces.SwInterfaceSetFlagsReply)
+ if err = api.RetvalToVPPApiError(reply3.Retval); err != nil {
+ return err
+ }
+
+ // Get host mac
+ netIntf, err := net.InterfaceByName(tap.Name())
+ if err == nil {
+ tap.HwAddress, _ = ethernet_types.ParseMacAddress(netIntf.HardwareAddr.String())
+ }
+
+ return nil
+}
+
+func (vpp *VppInstance) saveLogs() {
+ logTarget := vpp.getSuite().getLogDirPath() + "vppinstance-" + vpp.Container.Name + ".log"
+ logSource := vpp.Container.GetHostWorkDir() + defaultLogFilePath
+ cmd := exec.Command("cp", logSource, logTarget)
+ vpp.getSuite().Log(cmd.String())
+ cmd.Run()
+}
+
+func (vpp *VppInstance) Disconnect() {
+ vpp.Connection.Disconnect()
+ vpp.ApiStream.Close()
+}
+
+func (vpp *VppInstance) setDefaultCpuConfig() {
+ vpp.CpuConfig.PinMainCpu = true
+ vpp.CpuConfig.PinWorkersCorelist = true
+ vpp.CpuConfig.SkipCores = 0
+}
+
+func (vpp *VppInstance) generateVPPCpuConfig() string {
+ var c Stanza
+ var s string
+ startCpu := 0
+ if len(vpp.Cpus) < 1 {
+ return ""
+ }
+
+ c.NewStanza("cpu")
+
+ // If skip-cores is valid, use as start value to assign main/workers CPUs
+ if vpp.CpuConfig.SkipCores != 0 {
+ c.Append(fmt.Sprintf("skip-cores %d", vpp.CpuConfig.SkipCores))
+ vpp.getSuite().Log(fmt.Sprintf("skip-cores %d", vpp.CpuConfig.SkipCores))
+ }
+
+ if len(vpp.Cpus) > vpp.CpuConfig.SkipCores {
+ startCpu = vpp.CpuConfig.SkipCores
+ }
+
+ if vpp.CpuConfig.PinMainCpu {
+ c.Append(fmt.Sprintf("main-core %d", vpp.Cpus[startCpu]))
+ vpp.getSuite().Log(fmt.Sprintf("main-core %d", vpp.Cpus[startCpu]))
+ }
+
+ workers := vpp.Cpus[startCpu+1:]
+
+ if len(workers) > 0 {
+ if vpp.CpuConfig.PinWorkersCorelist {
+ for i := 0; i < len(workers); i++ {
+ if i != 0 {
+ s = s + ", "
+ }
+ s = s + fmt.Sprintf("%d", workers[i])
+ }
+ c.Append(fmt.Sprintf("corelist-workers %s", s))
+ vpp.getSuite().Log("corelist-workers " + s)
+ } else {
+ s = fmt.Sprintf("%d", len(workers))
+ c.Append(fmt.Sprintf("workers %s", s))
+ vpp.getSuite().Log("workers " + s)
+ }
+ }
+
+ return c.Close().ToString()
+}
+
+// EnableMemoryTrace enables memory traces of VPP main-heap
+func (vpp *VppInstance) EnableMemoryTrace() {
+ vpp.getSuite().Log(vpp.Vppctl("memory-trace on main-heap"))
+}
+
+// GetMemoryTrace dumps memory traces for analysis
+func (vpp *VppInstance) GetMemoryTrace() ([]VppMemTrace, error) {
+ var trace []VppMemTrace
+ vpp.getSuite().Log(vpp.Vppctl("save memory-trace trace.json"))
+ err := vpp.Container.GetFile("/tmp/trace.json", "/tmp/trace.json")
+ if err != nil {
+ return nil, err
+ }
+ fileBytes, err := os.ReadFile("/tmp/trace.json")
+ if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(fileBytes, &trace)
+ if err != nil {
+ return nil, err
+ }
+ return trace, nil
+}
+
+// memTracesSuppressCli filter out CLI related samples
+func memTracesSuppressCli(traces []VppMemTrace) []VppMemTrace {
+ var filtered []VppMemTrace
+ for i := 0; i < len(traces); i++ {
+ isCli := false
+ for j := 0; j < len(traces[i].Traceback); j++ {
+ if strings.Contains(traces[i].Traceback[j], "unix_cli") {
+ isCli = true
+ break
+ }
+ }
+ if !isCli {
+ filtered = append(filtered, traces[i])
+ }
+ }
+ return filtered
+}
+
+// MemLeakCheck compares memory traces at different point in time, analyzes if memory leaks happen and produces report
+func (vpp *VppInstance) MemLeakCheck(first, second []VppMemTrace) {
+ totalBytes := 0
+ totalCounts := 0
+ trace1 := memTracesSuppressCli(first)
+ trace2 := memTracesSuppressCli(second)
+ report := ""
+ for i := 0; i < len(trace2); i++ {
+ match := false
+ for j := 0; j < len(trace1); j++ {
+ if trace1[j].Sample == trace2[i].Sample {
+ if trace2[i].Size > trace1[j].Size {
+ deltaBytes := trace2[i].Size - trace1[j].Size
+ deltaCounts := trace2[i].Count - trace1[j].Count
+ report += fmt.Sprintf("grow %d byte(s) in %d allocation(s) from:\n", deltaBytes, deltaCounts)
+ for j := 0; j < len(trace2[i].Traceback); j++ {
+ report += fmt.Sprintf("\t#%d %s\n", j, trace2[i].Traceback[j])
+ }
+ totalBytes += deltaBytes
+ totalCounts += deltaCounts
+ }
+ match = true
+ break
+ }
+ }
+ if !match {
+ report += fmt.Sprintf("\nleak of %d byte(s) in %d allocation(s) from:\n", trace2[i].Size, trace2[i].Count)
+ for j := 0; j < len(trace2[i].Traceback); j++ {
+ report += fmt.Sprintf("\t#%d %s\n", j, trace2[i].Traceback[j])
+ }
+ totalBytes += trace2[i].Size
+ totalCounts += trace2[i].Count
+ }
+ }
+ summary := fmt.Sprintf("\nSUMMARY: %d byte(s) leaked in %d allocation(s)\n", totalBytes, totalCounts)
+ AddReportEntry(summary, report)
+}
+
+// CollectEventLogs saves event logs to the test execution directory
+func (vpp *VppInstance) CollectEventLogs() {
+ vpp.getSuite().Log(vpp.Vppctl("event-logger save event_log"))
+ targetDir := vpp.Container.Suite.getLogDirPath()
+ err := vpp.Container.GetFile("/tmp/event_log", targetDir+"/"+vpp.Container.Name+"-event_log")
+ if err != nil {
+ vpp.getSuite().Log(fmt.Sprint(err))
+ }
+}
+
+// EnablePcapTrace enables packet capture on all interfaces and maximum 10000 packets
+func (vpp *VppInstance) EnablePcapTrace() {
+ vpp.getSuite().Log(vpp.Vppctl("pcap trace rx tx max 10000 intfc any file vppTest.pcap"))
+}
+
+// CollectPcapTrace saves pcap trace to the test execution directory
+func (vpp *VppInstance) CollectPcapTrace() {
+ vpp.getSuite().Log(vpp.Vppctl("pcap trace off"))
+ targetDir := vpp.Container.Suite.getLogDirPath()
+ err := vpp.Container.GetFile("/tmp/vppTest.pcap", targetDir+"/"+vpp.Container.Name+".pcap")
+ if err != nil {
+ vpp.getSuite().Log(fmt.Sprint(err))
+ }
+}
diff --git a/extras/hs-test/iperf_linux_test.go b/extras/hs-test/iperf_linux_test.go
new file mode 100644
index 00000000000..14422fe5efa
--- /dev/null
+++ b/extras/hs-test/iperf_linux_test.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+ "fmt"
+
+ . "fd.io/hs-test/infra"
+ . "github.com/onsi/ginkgo/v2"
+)
+
+func init() {
+ RegisterIperfTests(IperfLinuxTest)
+}
+
+func IperfLinuxTest(s *IperfSuite) {
+ serverContainer := s.GetContainerByName(ServerIperfContainerName)
+ serverIpAddress := s.GetInterfaceByName(ServerIperfInterfaceName).Ip4AddressString()
+ clientContainer := s.GetContainerByName(ClientIperfContainerName)
+ clientIpAddress := s.GetInterfaceByName(ClientIperfInterfaceName).Ip4AddressString()
+
+ clnCh := make(chan error)
+ stopServerCh := make(chan struct{})
+ srvCh := make(chan error, 1)
+ clnRes := make(chan string, 1)
+
+ defer func() {
+ stopServerCh <- struct{}{}
+ }()
+
+ go func() {
+ defer GinkgoRecover()
+ cmd := "iperf3 -4 -s -B " + serverIpAddress + " -p " + s.GetPortFromPpid()
+ s.StartServerApp(serverContainer, "iperf3", cmd, srvCh, stopServerCh)
+ }()
+ err := <-srvCh
+ s.AssertNil(err, fmt.Sprint(err))
+ s.Log("server running")
+
+ go func() {
+ defer GinkgoRecover()
+ cmd := "iperf3 -c " + serverIpAddress + " -B " + clientIpAddress +
+ " -u -l 1460 -b 10g -p " + s.GetPortFromPpid()
+ s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
+ }()
+
+ s.Log(<-clnRes)
+ err = <-clnCh
+ s.AssertNil(err, "err: '%s'", err)
+}
diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go
index 24d2de39485..03636b11191 100644
--- a/extras/hs-test/ldp_test.go
+++ b/extras/hs-test/ldp_test.go
@@ -2,83 +2,89 @@ package main
import (
"fmt"
- "os"
+ . "fd.io/hs-test/infra"
. "github.com/onsi/ginkgo/v2"
)
func init() {
- registerVethTests(LDPreloadIperfVppTest)
+ RegisterLdpTests(LDPreloadIperfVppTest, LDPreloadIperfVppInterruptModeTest, RedisBenchmarkTest)
}
-func LDPreloadIperfVppTest(s *VethsSuite) {
- var clnVclConf, srvVclConf Stanza
-
- serverContainer := s.getContainerByName("server-vpp")
- serverVclFileName := serverContainer.getHostWorkDir() + "/vcl_srv.conf"
-
- clientContainer := s.getContainerByName("client-vpp")
- clientVclFileName := clientContainer.getHostWorkDir() + "/vcl_cln.conf"
+func LDPreloadIperfVppInterruptModeTest(s *LdpSuite) {
+ LDPreloadIperfVppTest(s)
+}
- ldpreload := "LD_PRELOAD=../../build-root/build-vpp-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so"
+func LDPreloadIperfVppTest(s *LdpSuite) {
+ clientContainer := s.GetContainerByName("client-vpp")
+ serverContainer := s.GetContainerByName("server-vpp")
stopServerCh := make(chan struct{}, 1)
srvCh := make(chan error, 1)
clnCh := make(chan error)
+ clnRes := make(chan string, 1)
- s.log("starting VPPs")
-
- clientAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
- clientContainer.getHostWorkDir())
- err := clnVclConf.
- newStanza("vcl").
- append("rx-fifo-size 4000000").
- append("tx-fifo-size 4000000").
- append("app-scope-local").
- append("app-scope-global").
- append("use-mq-eventfd").
- append(clientAppSocketApi).close().
- saveToFile(clientVclFileName)
- s.assertNil(err, fmt.Sprint(err))
-
- serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
- serverContainer.getHostWorkDir())
- err = srvVclConf.
- newStanza("vcl").
- append("rx-fifo-size 4000000").
- append("tx-fifo-size 4000000").
- append("app-scope-local").
- append("app-scope-global").
- append("use-mq-eventfd").
- append(serverAppSocketApi).close().
- saveToFile(serverVclFileName)
- s.assertNil(err, fmt.Sprint(err))
-
- s.log("attaching server to vpp")
-
- srvEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+serverVclFileName)
go func() {
defer GinkgoRecover()
- s.startServerApp(srvCh, stopServerCh, srvEnv)
+ cmd := "iperf3 -4 -s -p " + s.GetPortFromPpid()
+ s.StartServerApp(serverContainer, "iperf3", cmd, srvCh, stopServerCh)
}()
- err = <-srvCh
- s.assertNil(err, fmt.Sprint(err))
+ err := <-srvCh
+ s.AssertNil(err, fmt.Sprint(err))
- s.log("attaching client to vpp")
- var clnRes = make(chan string, 1)
- clnEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+clientVclFileName)
- serverVethAddress := s.getInterfaceByName(serverInterfaceName).ip4AddressString()
+ serverVethAddress := s.GetInterfaceByName(ServerInterfaceName).Ip4AddressString()
go func() {
defer GinkgoRecover()
- s.startClientApp(serverVethAddress, clnEnv, clnCh, clnRes)
+ cmd := "iperf3 -c " + serverVethAddress + " -u -l 1460 -b 10g -p " + s.GetPortFromPpid()
+ s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
}()
- s.log(<-clnRes)
+ s.Log(<-clnRes)
// wait for client's result
err = <-clnCh
- s.assertNil(err, fmt.Sprint(err))
+ s.AssertNil(err, fmt.Sprint(err))
// stop server
stopServerCh <- struct{}{}
}
+
+func RedisBenchmarkTest(s *LdpSuite) {
+ s.SkipIfMultiWorker()
+
+ serverContainer := s.GetContainerByName("server-vpp")
+ clientContainer := s.GetContainerByName("client-vpp")
+
+ serverVethAddress := s.GetInterfaceByName(ServerInterfaceName).Ip4AddressString()
+ runningSrv := make(chan error)
+ doneSrv := make(chan struct{})
+ clnCh := make(chan error)
+ clnRes := make(chan string, 1)
+
+ go func() {
+ defer GinkgoRecover()
+ cmd := "redis-server --daemonize yes --protected-mode no --bind " + serverVethAddress
+ s.StartServerApp(serverContainer, "redis-server", cmd, runningSrv, doneSrv)
+ }()
+
+ err := <-runningSrv
+ s.AssertNil(err)
+
+ go func() {
+ defer GinkgoRecover()
+ var cmd string
+ if *NConfiguredCpus == 1 {
+ cmd = "redis-benchmark --threads 1 -h " + serverVethAddress
+ } else {
+ cmd = "redis-benchmark --threads " + fmt.Sprint(*NConfiguredCpus) + "-h " + serverVethAddress
+ }
+ s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
+ }()
+
+ s.Log(<-clnRes)
+ // wait for client's result
+ err = <-clnCh
+ s.AssertNil(err, fmt.Sprint(err))
+ // stop server
+ doneSrv <- struct{}{}
+}
diff --git a/extras/hs-test/linux_iperf_test.go b/extras/hs-test/linux_iperf_test.go
deleted file mode 100644
index e323f7fb721..00000000000
--- a/extras/hs-test/linux_iperf_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package main
-
-import (
- "fmt"
-
- . "github.com/onsi/ginkgo/v2"
-)
-
-func init() {
- registerTapTests(LinuxIperfTest)
-}
-
-func LinuxIperfTest(s *TapSuite) {
- clnCh := make(chan error)
- stopServerCh := make(chan struct{})
- srvCh := make(chan error, 1)
- clnRes := make(chan string, 1)
- defer func() {
- stopServerCh <- struct{}{}
- }()
-
- go func() {
- defer GinkgoRecover()
- s.startServerApp(srvCh, stopServerCh, nil)
- }()
- err := <-srvCh
- s.assertNil(err, fmt.Sprint(err))
- s.log("server running")
-
- ipAddress := s.getInterfaceByName(tapInterfaceName).ip4AddressString()
- go func() {
- defer GinkgoRecover()
- s.startClientApp(ipAddress, nil, clnCh, clnRes)
- }()
- s.log("client running")
- s.log(<-clnRes)
- err = <-clnCh
- s.assertNil(err, "err: '%s', ip: '%s'", err, ipAddress)
- s.log("Test completed")
-}
diff --git a/extras/hs-test/mem_leak_test.go b/extras/hs-test/mem_leak_test.go
new file mode 100644
index 00000000000..76966ae968a
--- /dev/null
+++ b/extras/hs-test/mem_leak_test.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ . "fd.io/hs-test/infra"
+ "fmt"
+)
+
+func init() {
+ RegisterNoTopoSoloTests(MemLeakTest)
+}
+
+func MemLeakTest(s *NoTopoSuite) {
+ s.SkipUnlessLeakCheck()
+ vpp := s.GetContainerByName("vpp").VppInstance
+ /* no goVPP less noise */
+ vpp.Disconnect()
+ vpp.EnableMemoryTrace()
+ traces1, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.Vppctl("test mem-leak")
+ traces2, err := vpp.GetMemoryTrace()
+ s.AssertNil(err, fmt.Sprint(err))
+ vpp.MemLeakCheck(traces1, traces2)
+}
diff --git a/extras/hs-test/mirroring_test.go b/extras/hs-test/mirroring_test.go
deleted file mode 100644
index 6c5a860b01c..00000000000
--- a/extras/hs-test/mirroring_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package main
-
-import (
- "github.com/edwarnicke/exechelper"
-)
-
-func init() {
- registerNginxTests(MirroringTest)
-}
-
-func MirroringTest(s *NginxSuite) {
- proxyAddress := s.getInterfaceByName(mirroringClientInterfaceName).peer.ip4AddressString()
-
- path := "/64B.json"
-
- testCommand := "wrk -c 20 -t 10 -d 10 http://" + proxyAddress + ":80" + path
- s.log(testCommand)
- o, _ := exechelper.Output(testCommand)
- s.log(string(o))
- s.assertNotEmpty(o)
-
- vppProxyContainer := s.getContainerByName(vppProxyContainerName)
- s.assertEqual(0, vppProxyContainer.vppInstance.GetSessionStat("no lcl port"))
-}
diff --git a/extras/hs-test/nginx_test.go b/extras/hs-test/nginx_test.go
new file mode 100644
index 00000000000..fa6afda58fc
--- /dev/null
+++ b/extras/hs-test/nginx_test.go
@@ -0,0 +1,136 @@
+package main
+
+import (
+ . "fd.io/hs-test/infra"
+ "fmt"
+ . "github.com/onsi/ginkgo/v2"
+ "os"
+ "strings"
+)
+
+func init() {
+ RegisterNoTopoTests(NginxHttp3Test, NginxAsServerTest, NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest,
+ NginxPerfCpsInterruptModeTest, NginxPerfRpsInterruptModeTest, NginxPerfWrkInterruptModeTest)
+}
+
+func NginxHttp3Test(s *NoTopoSuite) {
+ s.SkipUnlessExtendedTestsBuilt()
+
+ query := "index.html"
+ nginxCont := s.GetContainerByName("nginx-http3")
+ nginxCont.Run()
+
+ vpp := s.GetContainerByName("vpp").VppInstance
+ vpp.WaitForApp("nginx-", 5)
+ serverAddress := s.VppAddr()
+
+ defer func() { os.Remove(query) }()
+ curlCont := s.GetContainerByName("curl")
+ args := fmt.Sprintf("curl --noproxy '*' --local-port 55444 --http3-only -k https://%s:8443/%s", serverAddress, query)
+ curlCont.ExtraRunningArgs = args
+ curlCont.Run()
+ o, err := curlCont.GetOutput()
+ s.Log(o)
+ s.AssertEmpty(err)
+ s.AssertContains(o, "<http>", "<http> not found in the result!")
+}
+func NginxAsServerTest(s *NoTopoSuite) {
+ query := "return_ok"
+ finished := make(chan error, 1)
+
+ nginxCont := s.GetContainerByName("nginx")
+ nginxCont.Run()
+
+ vpp := s.GetContainerByName("vpp").VppInstance
+ vpp.WaitForApp("nginx-", 5)
+
+ serverAddress := s.VppAddr()
+
+ defer func() { os.Remove(query) }()
+ go func() {
+ defer GinkgoRecover()
+ s.StartWget(finished, serverAddress, "80", query, "")
+ }()
+ s.AssertNil(<-finished)
+}
+
+func parseString(s, pattern string) string {
+ temp := strings.Split(s, "\n")
+ for _, item := range temp {
+ if strings.Contains(item, pattern) {
+ return item
+ }
+ }
+ return ""
+}
+
+func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error {
+ nRequests := 1000000
+ nClients := 1000
+
+ serverAddress := s.VppAddr()
+
+ vpp := s.GetContainerByName("vpp").VppInstance
+
+ nginxCont := s.GetContainerByName(SingleTopoContainerNginx)
+ nginxCont.Run()
+ vpp.WaitForApp("nginx-", 5)
+
+ if ab_or_wrk == "ab" {
+ abCont := s.GetContainerByName("ab")
+ args := fmt.Sprintf("-n %d -c %d", nRequests, nClients)
+ if mode == "rps" {
+ args += " -k"
+ } else if mode != "cps" {
+ return fmt.Errorf("invalid mode %s; expected cps/rps", mode)
+ }
+ // don't exit on socket receive errors
+ args += " -r"
+ args += " http://" + serverAddress + ":80/64B.json"
+ abCont.ExtraRunningArgs = args
+ abCont.Run()
+ o, err := abCont.GetOutput()
+ rps := parseString(o, "Requests per second:")
+ s.Log(rps)
+ s.AssertContains(err, "Finished "+fmt.Sprint(nRequests))
+ } else {
+ wrkCont := s.GetContainerByName("wrk")
+ args := fmt.Sprintf("-c %d -t 2 -d 30 http://%s:80/64B.json", nClients,
+ serverAddress)
+ wrkCont.ExtraRunningArgs = args
+ wrkCont.Run()
+ s.Log("Please wait for 30s, test is running.")
+ o, err := wrkCont.GetOutput()
+ rps := parseString(o, "requests")
+ s.Log(rps)
+ s.Log(err)
+ s.AssertEmpty(err, "err: '%s', output: '%s'", err, o)
+ }
+ return nil
+}
+
+func NginxPerfCpsInterruptModeTest(s *NoTopoSuite) {
+ NginxPerfCpsTest(s)
+}
+
+// unstable with multiple workers
+func NginxPerfCpsTest(s *NoTopoSuite) {
+ s.SkipIfMultiWorker()
+ s.AssertNil(runNginxPerf(s, "cps", "ab"))
+}
+
+func NginxPerfRpsInterruptModeTest(s *NoTopoSuite) {
+ NginxPerfRpsTest(s)
+}
+
+func NginxPerfRpsTest(s *NoTopoSuite) {
+ s.AssertNil(runNginxPerf(s, "rps", "ab"))
+}
+
+func NginxPerfWrkInterruptModeTest(s *NoTopoSuite) {
+ NginxPerfWrkTest(s)
+}
+
+func NginxPerfWrkTest(s *NoTopoSuite) {
+ s.AssertNil(runNginxPerf(s, "", "wrk"))
+}
diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go
index ac5f94c8535..7ec97c76c02 100644
--- a/extras/hs-test/proxy_test.go
+++ b/extras/hs-test/proxy_test.go
@@ -1,115 +1,70 @@
package main
import (
+ . "fd.io/hs-test/infra"
"fmt"
- "os"
-
- "github.com/edwarnicke/exechelper"
- . "github.com/onsi/ginkgo/v2"
)
func init() {
- registerNsTests(VppProxyHttpTcpTest, VppProxyHttpTlsTest, EnvoyProxyHttpTcpTest)
+ RegisterVppProxyTests(VppProxyHttpGetTcpTest, VppProxyHttpGetTlsTest, VppProxyHttpPutTcpTest, VppProxyHttpPutTlsTest)
+ RegisterEnvoyProxyTests(EnvoyProxyHttpGetTcpTest, EnvoyProxyHttpPutTcpTest)
+ RegisterNginxProxyTests(NginxMirroringTest)
}
-func testProxyHttpTcp(s *NsSuite, proto string) error {
- var outputFile string = "test" + s.pid + ".data"
- var srcFilePid string = "httpTestFile" + s.pid
- const srcFileNoPid = "httpTestFile"
- const fileSize string = "10M"
- stopServer := make(chan struct{}, 1)
- serverRunning := make(chan struct{}, 1)
- serverNetns := s.getNetNamespaceByName("srv")
- clientNetns := s.getNetNamespaceByName("cln")
-
- // create test file
- err := exechelper.Run(fmt.Sprintf("ip netns exec %s truncate -s %s %s", serverNetns, fileSize, srcFilePid))
- s.assertNil(err, "failed to run truncate command: "+fmt.Sprint(err))
- defer func() { os.Remove(srcFilePid) }()
-
- s.log("test file created...")
-
- go func() {
- defer GinkgoRecover()
- s.startHttpServer(serverRunning, stopServer, ":666", serverNetns)
- }()
- // TODO better error handling and recovery
- <-serverRunning
-
- defer func(chan struct{}) {
- stopServer <- struct{}{}
- }(stopServer)
-
- s.log("http server started...")
-
- clientVeth := s.getInterfaceByName(clientInterface)
- c := fmt.Sprintf("ip netns exec %s wget --no-proxy --retry-connrefused"+
- " --retry-on-http-error=503 --tries=10 -O %s ", clientNetns, outputFile)
- if proto == "tls" {
- c += " --secure-protocol=TLSv1_3 --no-check-certificate https://"
- }
- c += fmt.Sprintf("%s:555/%s", clientVeth.ip4AddressString(), srcFileNoPid)
- s.log(c)
- _, err = exechelper.CombinedOutput(c)
-
- defer func() { os.Remove(outputFile) }()
-
- s.assertNil(err, "failed to run wget: '%s', cmd: %s", err, c)
- stopServer <- struct{}{}
-
- s.assertNil(assertFileSize(outputFile, srcFilePid))
- return nil
+func configureVppProxy(s *VppProxySuite, proto string, proxyPort uint16) {
+ vppProxy := s.GetContainerByName(VppProxyContainerName).VppInstance
+ output := vppProxy.Vppctl(
+ "test proxy server server-uri %s://%s/%d client-uri tcp://%s/%d",
+ proto,
+ s.VppProxyAddr(),
+ proxyPort,
+ s.NginxAddr(),
+ s.NginxPort(),
+ )
+ s.Log("proxy configured: " + output)
}
-func configureVppProxy(s *NsSuite, proto string) {
- serverVeth := s.getInterfaceByName(serverInterface)
- clientVeth := s.getInterfaceByName(clientInterface)
+func VppProxyHttpGetTcpTest(s *VppProxySuite) {
+ var proxyPort uint16 = 8080
+ configureVppProxy(s, "tcp", proxyPort)
+ uri := fmt.Sprintf("http://%s:%d/httpTestFile", s.VppProxyAddr(), proxyPort)
+ s.CurlDownloadResource(uri)
+}
- testVppProxy := s.getContainerByName("vpp").vppInstance
- output := testVppProxy.vppctl(
- "test proxy server server-uri %s://%s/555 client-uri tcp://%s/666",
- proto,
- clientVeth.ip4AddressString(),
- serverVeth.peer.ip4AddressString(),
- )
- s.log("proxy configured: " + output)
+func VppProxyHttpGetTlsTest(s *VppProxySuite) {
+ var proxyPort uint16 = 8080
+ configureVppProxy(s, "tls", proxyPort)
+ uri := fmt.Sprintf("https://%s:%d/httpTestFile", s.VppProxyAddr(), proxyPort)
+ s.CurlDownloadResource(uri)
}
-func VppProxyHttpTcpTest(s *NsSuite) {
- proto := "tcp"
- configureVppProxy(s, proto)
- err := testProxyHttpTcp(s, proto)
- s.assertNil(err, fmt.Sprint(err))
+func VppProxyHttpPutTcpTest(s *VppProxySuite) {
+ var proxyPort uint16 = 8080
+ configureVppProxy(s, "tcp", proxyPort)
+ uri := fmt.Sprintf("http://%s:%d/upload/testFile", s.VppProxyAddr(), proxyPort)
+ s.CurlUploadResource(uri, CurlContainerTestFile)
}
-func VppProxyHttpTlsTest(s *NsSuite) {
- proto := "tls"
- configureVppProxy(s, proto)
- err := testProxyHttpTcp(s, proto)
- s.assertNil(err, fmt.Sprint(err))
+func VppProxyHttpPutTlsTest(s *VppProxySuite) {
+ var proxyPort uint16 = 8080
+ configureVppProxy(s, "tls", proxyPort)
+ uri := fmt.Sprintf("https://%s:%d/upload/testFile", s.VppProxyAddr(), proxyPort)
+ s.CurlUploadResource(uri, CurlContainerTestFile)
}
-func configureEnvoyProxy(s *NsSuite) {
- envoyContainer := s.getContainerByName("envoy")
- err := envoyContainer.create()
- s.assertNil(err, "Error creating envoy container: %s", err)
+func EnvoyProxyHttpGetTcpTest(s *EnvoyProxySuite) {
+ uri := fmt.Sprintf("http://%s:%d/httpTestFile", s.ProxyAddr(), s.ProxyPort())
+ s.CurlDownloadResource(uri)
+}
- serverVeth := s.getInterfaceByName(serverInterface)
- address := struct {
- Server string
- }{
- Server: serverVeth.peer.ip4AddressString(),
- }
- envoyContainer.createConfig(
- "/etc/envoy/envoy.yaml",
- "resources/envoy/proxy.yaml",
- address,
- )
- s.assertNil(envoyContainer.start())
+func EnvoyProxyHttpPutTcpTest(s *EnvoyProxySuite) {
+ uri := fmt.Sprintf("http://%s:%d/upload/testFile", s.ProxyAddr(), s.ProxyPort())
+ s.CurlUploadResource(uri, CurlContainerTestFile)
}
-func EnvoyProxyHttpTcpTest(s *NsSuite) {
- configureEnvoyProxy(s)
- err := testProxyHttpTcp(s, "tcp")
- s.assertNil(err, fmt.Sprint(err))
+// broken when CPUS > 1
+func NginxMirroringTest(s *NginxProxySuite) {
+ s.SkipIfMultiWorker()
+ uri := fmt.Sprintf("http://%s:%d/httpTestFile", s.ProxyAddr(), s.ProxyPort())
+ s.CurlDownloadResource(uri)
}
diff --git a/extras/hs-test/raw_session_test.go b/extras/hs-test/raw_session_test.go
index 5c66df0b1ce..438b7ba03a5 100644
--- a/extras/hs-test/raw_session_test.go
+++ b/extras/hs-test/raw_session_test.go
@@ -1,40 +1,42 @@
package main
+import . "fd.io/hs-test/infra"
+
func init() {
- registerVethTests(VppEchoQuicTest, VppEchoTcpTest)
+ RegisterVethTests(VppEchoQuicTest, VppEchoTcpTest)
}
func VppEchoQuicTest(s *VethsSuite) {
- s.testVppEcho("quic")
+ testVppEcho(s, "quic")
}
// TODO: udp echo currently broken in vpp
func VppEchoUdpTest(s *VethsSuite) {
- s.testVppEcho("udp")
+ testVppEcho(s, "udp")
}
func VppEchoTcpTest(s *VethsSuite) {
- s.testVppEcho("tcp")
+ testVppEcho(s, "tcp")
}
-func (s *VethsSuite) testVppEcho(proto string) {
- serverVethAddress := s.getInterfaceByName(serverInterfaceName).ip4AddressString()
+func testVppEcho(s *VethsSuite, proto string) {
+ serverVethAddress := s.GetInterfaceByName(ServerInterfaceName).Ip4AddressString()
uri := proto + "://" + serverVethAddress + "/12344"
- echoSrvContainer := s.getContainerByName("server-app")
+ echoSrvContainer := s.GetContainerByName("server-app")
serverCommand := "vpp_echo server TX=RX" +
- " socket-name " + echoSrvContainer.getContainerWorkDir() + "/var/run/app_ns_sockets/default" +
+ " socket-name " + echoSrvContainer.GetContainerWorkDir() + "/var/run/app_ns_sockets/default" +
" use-app-socket-api" +
" uri " + uri
- s.log(serverCommand)
- echoSrvContainer.execServer(serverCommand)
+ s.Log(serverCommand)
+ echoSrvContainer.ExecServer(serverCommand)
- echoClnContainer := s.getContainerByName("client-app")
+ echoClnContainer := s.GetContainerByName("client-app")
clientCommand := "vpp_echo client" +
- " socket-name " + echoClnContainer.getContainerWorkDir() + "/var/run/app_ns_sockets/default" +
+ " socket-name " + echoClnContainer.GetContainerWorkDir() + "/var/run/app_ns_sockets/default" +
" use-app-socket-api uri " + uri
- s.log(clientCommand)
- o := echoClnContainer.exec(clientCommand)
- s.log(o)
+ s.Log(clientCommand)
+ o := echoClnContainer.Exec(clientCommand)
+ s.Log(o)
}
diff --git a/extras/hs-test/resources/curl/write_out_download b/extras/hs-test/resources/curl/write_out_download
new file mode 100644
index 00000000000..9fc7699c229
--- /dev/null
+++ b/extras/hs-test/resources/curl/write_out_download
@@ -0,0 +1 @@
+Download size: %{size_download} bytes\nDownload speed: %{speed_download} bytes per second\nGET response code: %{response_code}\n \ No newline at end of file
diff --git a/extras/hs-test/resources/curl/write_out_download_connect b/extras/hs-test/resources/curl/write_out_download_connect
new file mode 100644
index 00000000000..bd7c9152a0c
--- /dev/null
+++ b/extras/hs-test/resources/curl/write_out_download_connect
@@ -0,0 +1 @@
+Download size: %{size_download} bytes\nDownload speed: %{speed_download} bytes per second\nCONNECT response code: %{http_connect}\nGET response code: %{response_code}\n \ No newline at end of file
diff --git a/extras/hs-test/resources/curl/write_out_upload b/extras/hs-test/resources/curl/write_out_upload
new file mode 100644
index 00000000000..c2857aa6b0c
--- /dev/null
+++ b/extras/hs-test/resources/curl/write_out_upload
@@ -0,0 +1 @@
+Upload size: %{size_upload} bytes\nUpload speed: %{speed_upload} bytes per second\nPUT response code: %{response_code}\n \ No newline at end of file
diff --git a/extras/hs-test/resources/curl/write_out_upload_connect b/extras/hs-test/resources/curl/write_out_upload_connect
new file mode 100644
index 00000000000..35021655ec0
--- /dev/null
+++ b/extras/hs-test/resources/curl/write_out_upload_connect
@@ -0,0 +1 @@
+Upload size: %{size_upload} bytes\nUpload speed: %{speed_upload} bytes per second\nCONNECT response code: %{http_connect}\nPUT response code: %{response_code}\n \ No newline at end of file
diff --git a/extras/hs-test/resources/envoy/proxy.yaml b/extras/hs-test/resources/envoy/proxy.yaml
index 77da80d934d..67eb6909ed2 100644
--- a/extras/hs-test/resources/envoy/proxy.yaml
+++ b/extras/hs-test/resources/envoy/proxy.yaml
@@ -1,17 +1,15 @@
admin:
- access_log_path: /tmp/envoy.log
address:
socket_address:
address: 0.0.0.0
port_value: 8081
static_resources:
listeners:
- # define a reverse proxy on :10001 that always uses :80 as an origin.
- address:
socket_address:
protocol: TCP
address: 0.0.0.0
- port_value: 555
+ port_value: {{.ProxyPort}}
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
@@ -30,6 +28,13 @@ static_resources:
cluster: proxy_service
http_filters:
- name: envoy.filters.http.router
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ access_log:
+ - name: envoy.access_loggers.file
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
+ path: /tmp/vpp-envoy/{{.LogPrefix}}-access.log
clusters:
- name: proxy_service
connect_timeout: 0.25s
@@ -43,9 +48,8 @@ static_resources:
- endpoint:
address:
socket_address:
- # following address will be generated by Addresser during test run
- address: {{.Server}}
- port_value: 666
+ address: {{.ServerAddress}}
+ port_value: {{.ServerPort}}
bootstrap_extensions:
- name: envoy.extensions.vcl.vcl_socket_interface
typed_config:
diff --git a/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf b/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf
index 56debf5c290..7f6b09c6050 100644
--- a/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf
+++ b/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf
@@ -3,7 +3,7 @@ worker_processes 4;
worker_rlimit_nofile 102400;
daemon off;
-error_log /tmp/nginx/error.log;
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
events {
use epoll;
@@ -44,7 +44,8 @@ http {
}
server {
- listen 80;
+ access_log /tmp/nginx/{{.LogPrefix}}-access.log;
+ listen {{.Port}};
server_name {{.Proxy}};
server_tokens off;
diff --git a/extras/hs-test/resources/nginx/nginx_server.conf b/extras/hs-test/resources/nginx/nginx_server.conf
new file mode 100644
index 00000000000..26d58348039
--- /dev/null
+++ b/extras/hs-test/resources/nginx/nginx_server.conf
@@ -0,0 +1,43 @@
+master_process on;
+worker_rlimit_nofile 10240;
+worker_processes 2;
+daemon off;
+
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
+
+events {
+ use epoll;
+ worker_connections 10240;
+ accept_mutex off;
+ multi_accept off;
+}
+
+http {
+ keepalive_timeout 300s;
+ keepalive_requests 1000000;
+ client_body_timeout {{.Timeout}}s;
+ client_header_timeout {{.Timeout}}s;
+ send_timeout {{.Timeout}}s;
+ sendfile on;
+ server {
+ access_log /tmp/nginx/{{.LogPrefix}}-access.log;
+ listen {{.Port}};
+ server_name {{.Address}};
+ root /usr/share/nginx;
+ index index.html index.htm;
+ location ~ "/upload/([0-9a-zA-Z-.]*)$" {
+ alias /usr/share/nginx/upload/$1;
+ client_body_temp_path /tmp;
+ client_max_body_size 200M;
+ dav_methods PUT;
+ create_full_put_path off;
+ dav_access all:rw;
+ }
+ location /64B {
+ return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+ }
+ location / {
+ sendfile on;
+ }
+ }
+}
diff --git a/extras/hs-test/resources/nginx/nginx_server_mirroring.conf b/extras/hs-test/resources/nginx/nginx_server_mirroring.conf
index 4056801ea13..921eb2eefd5 100644
--- a/extras/hs-test/resources/nginx/nginx_server_mirroring.conf
+++ b/extras/hs-test/resources/nginx/nginx_server_mirroring.conf
@@ -3,6 +3,8 @@ worker_rlimit_nofile 10240;
worker_processes 2;
daemon off;
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
+
events {
use epoll;
worker_connections 10240;
@@ -15,18 +17,17 @@ http {
keepalive_requests 1000000;
sendfile on;
server {
+ access_log /tmp/nginx/{{.LogPrefix}}-access.log;
listen 8091;
listen 8092;
listen 8093;
root /usr/share/nginx;
index index.html index.htm;
- location /return_ok
- {
- return 200 '';
- }
- location /64B.json
- {
+ location /64B {
return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
}
+ location / {
+ sendfile on;
+ }
}
}
diff --git a/extras/hs-test/script/build_boringssl.sh b/extras/hs-test/script/build_boringssl.sh
index 441878a77ca..cca4e4a31d1 100755
--- a/extras/hs-test/script/build_boringssl.sh
+++ b/extras/hs-test/script/build_boringssl.sh
@@ -1,4 +1,4 @@
#!/bin/bash
-cd boringssl
+cd boringssl || exit 1
cmake -GNinja -B build
ninja -C build
diff --git a/extras/hs-test/script/build_hst.sh b/extras/hs-test/script/build_hst.sh
index 33a8393b8b5..9b11f5f0272 100755
--- a/extras/hs-test/script/build_hst.sh
+++ b/extras/hs-test/script/build_hst.sh
@@ -1,37 +1,51 @@
#!/usr/bin/env bash
-if [ $(lsb_release -is) != Ubuntu ]; then
+if [ "$(lsb_release -is)" != Ubuntu ]; then
echo "Host stack test framework is supported only on Ubuntu"
exit 1
fi
-if [ -z $(which ab) ]; then
+if [ -z "$(which ab)" ]; then
echo "Host stack test framework requires apache2-utils to be installed"
echo "It is recommended to run 'sudo make install-dep'"
exit 1
fi
-if [ -z $(which wrk) ]; then
+if [ -z "$(which wrk)" ]; then
echo "Host stack test framework requires wrk to be installed"
echo "It is recommended to run 'sudo make install-dep'"
exit 1
fi
export VPP_WS=../..
+OS_ARCH="$(uname -m)"
+DOCKER_BUILD_DIR="/scratch/docker-build"
+DOCKER_CACHE_DIR="${DOCKER_BUILD_DIR}/docker_cache"
+
+if [ -d "${DOCKER_BUILD_DIR}" ] ; then
+ mkdir -p "${DOCKER_CACHE_DIR}"
+ DOCKER_HST_BUILDER="hst_builder"
+ set -x
+ if ! docker buildx ls --format "{{.Name}}" | grep -q "${DOCKER_HST_BUILDER}"; then
+ docker buildx create --name=${DOCKER_HST_BUILDER} --driver=docker-container --use --bootstrap || true
+ fi
+ set -x
+ DOCKER_CACHE_ARGS="--builder=${DOCKER_HST_BUILDER} --load --cache-to type=local,dest=${DOCKER_CACHE_DIR},mode=max --cache-from type=local,src=${DOCKER_CACHE_DIR}"
+fi
if [ "$1" == "debug" ]; then
VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp_debug-native/vpp
+elif [ "$1" == "gcov" ]; then
+ VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp_gcov-native/vpp
else
VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp-native/vpp
fi
echo "Taking build objects from ${VPP_BUILD_ROOT}"
-if [ -z "$UBUNTU_VERSION" ] ; then
- export UBUNTU_VERSION=$(lsb_release -rs)
-fi
+export UBUNTU_VERSION=${UBUNTU_VERSION:-"$(lsb_release -rs)"}
echo "Ubuntu version is set to ${UBUNTU_VERSION}"
-export HST_LDPRELOAD=${VPP_BUILD_ROOT}/lib/x86_64-linux-gnu/libvcl_ldpreload.so
+export HST_LDPRELOAD=${VPP_BUILD_ROOT}/lib/${OS_ARCH}-linux-gnu/libvcl_ldpreload.so
echo "HST_LDPRELOAD is set to ${HST_LDPRELOAD}"
export PATH=${VPP_BUILD_ROOT}/bin:$PATH
@@ -45,7 +59,7 @@ rm -rf vpp-data/lib/* || true
cp ${VPP_BUILD_ROOT}/bin/* ${bin}
res+=$?
-cp -r ${VPP_BUILD_ROOT}/lib/x86_64-linux-gnu/* ${lib}
+cp -r ${VPP_BUILD_ROOT}/lib/"${OS_ARCH}"-linux-gnu/* ${lib}
res+=$?
if [ $res -ne 0 ]; then
echo "Failed to copy VPP files. Is VPP built? Try running 'make build' in VPP directory."
@@ -55,18 +69,21 @@ fi
docker_build () {
tag=$1
dockername=$2
- docker build --build-arg UBUNTU_VERSION \
- --build-arg http_proxy=$HTTP_PROXY \
- --build-arg https_proxy=$HTTP_PROXY \
- --build-arg HTTP_PROXY=$HTTP_PROXY \
- --build-arg HTTPS_PROXY=$HTTP_PROXY \
- -t $tag -f docker/Dockerfile.$dockername .
+ set -ex
+ # shellcheck disable=2086
+ docker buildx build ${DOCKER_CACHE_ARGS} \
+ --build-arg UBUNTU_VERSION \
+ --build-arg http_proxy="$HTTP_PROXY" \
+ --build-arg https_proxy="$HTTP_PROXY" \
+ --build-arg HTTP_PROXY="$HTTP_PROXY" \
+ --build-arg HTTPS_PROXY="$HTTP_PROXY" \
+ -t "$tag" -f docker/Dockerfile."$dockername" .
+ set +ex
}
docker_build hs-test/vpp vpp
docker_build hs-test/nginx-ldp nginx
docker_build hs-test/nginx-server nginx-server
-docker_build hs-test/build build
docker_build hs-test/curl curl
if [ "$HST_EXTENDED_TESTS" = true ] ; then
docker_build hs-test/nginx-http3 nginx-http3
@@ -75,5 +92,6 @@ fi
# cleanup detached images
images=$(docker images --filter "dangling=true" -q --no-trunc)
if [ "$images" != "" ]; then
+ # shellcheck disable=SC2086
docker rmi $images
fi
diff --git a/extras/hs-test/script/build_nginx.sh b/extras/hs-test/script/build_nginx.sh
index 69d366aab0e..f21201c4297 100755
--- a/extras/hs-test/script/build_nginx.sh
+++ b/extras/hs-test/script/build_nginx.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-cd nginx
+cd nginx || exit 1
./auto/configure --with-debug --with-http_v3_module --with-cc-opt="-I../boringssl/include" --with-ld-opt="-L../boringssl/build/ssl -L../boringssl/build/crypto" --without-http_rewrite_module --without-http_gzip_module
make
make install
diff --git a/extras/hs-test/script/compress.sh b/extras/hs-test/script/compress.sh
index 92a2fbd6789..09db9b6720d 100644..100755
--- a/extras/hs-test/script/compress.sh
+++ b/extras/hs-test/script/compress.sh
@@ -1,34 +1,37 @@
#!/usr/bin/env bash
-if [ "${COMPRESS_FAILED_TEST_LOGS}" == "yes" -a -s "${HS_SUMMARY}/failed-summary.log" ]
+# if failed-summary.log is not empty, exit status = 1
+if [ -s "${HS_ROOT}/summary/failed-summary.log" ]
then
- echo -n "Copying docker logs..."
- dirs=$(jq -r '.[0] | .SpecReports[] | select(.State == "failed") | .LeafNodeText' ${HS_SUMMARY}/report.json)
- for dirName in $dirs; do
- logDir=/tmp/hs-test/$dirName
- if [ -d "$logDir" ]; then
- mkdir -p $WORKSPACE/archives/summary
- cp -r $logDir $WORKSPACE/archives/summary/
- fi
- done
- echo "Done."
-
- if [ -n "$WORKSPACE" ]
+ if [ -n "${WORKSPACE}" ]
then
- echo -n "Copying failed test logs into build log archive directory ($WORKSPACE/archives)... "
- mkdir -p $WORKSPACE/archives/summary
- cp -a ${HS_SUMMARY}/* $WORKSPACE/archives/summary
- echo "Done."
- fi
+ echo -n "Copying docker logs..."
+ dirs=$(jq -r '.[0] | .SpecReports[] | select(.State == "failed") | .LeafNodeText | split("/")[1]' ${HS_ROOT}/summary/report.json)
+ for dirName in $dirs; do
+ logDir=/tmp/hs-test/$dirName
+ if [ -d "$logDir" ]; then
+ mkdir -p ${WORKSPACE}/archives/summary
+ rsync -a --exclude 'volumes' $logDir ${WORKSPACE}/archives/summary/
+ fi
+ done
+ echo "Done."
+
+ echo -n "Copying failed test logs into build log archive directory (${WORKSPACE}/archives)... "
+ mkdir -p ${WORKSPACE}/archives/summary
+ cp -a ${HS_ROOT}/summary/* ${WORKSPACE}/archives/summary
+ echo "Done."
- echo -n "Compressing files in $WORKSPACE/archives from test runs... "
- cd $WORKSPACE/archives
- find . -type f \( -name "*.json" -o -name "*.log" \) -exec gzip {} \;
- echo "Done."
+ echo -n "Compressing files in ${WORKSPACE}/archives from test runs... "
+ cd ${WORKSPACE}/archives
+ find . -type f \( -name "*.json" -o -name "*.log" \) -exec gzip {} \;
+ echo "Done."
+ else
+ echo "Not compressing files in temporary directories from test runs."
+ fi
+ echo "*************************** SUMMARY ***************************"
+ cat "${HS_ROOT}/summary/failed-summary.log"
+ exit 1
else
- echo "Not compressing files in temporary directories from test runs."
- exit 0
+ exit $1
fi
-
-exit 1 \ No newline at end of file
diff --git a/extras/hs-test/script/nginx_ldp.sh b/extras/hs-test/script/nginx_ldp.sh
index 4a22e14aaf7..416aa5499af 100755
--- a/extras/hs-test/script/nginx_ldp.sh
+++ b/extras/hs-test/script/nginx_ldp.sh
@@ -1,3 +1,4 @@
#!/usr/bin/env bash
-LD_PRELOAD=$LDP $@ 2>&1 > /proc/1/fd/1
+# shellcheck disable=SC2068
+$1 -v && LD_PRELOAD=$LDP $@ > /proc/1/fd/1 2>&1
diff --git a/extras/hs-test/script/nginx_server_entrypoint.sh b/extras/hs-test/script/nginx_server_entrypoint.sh
new file mode 100755
index 00000000000..6581c8b74d3
--- /dev/null
+++ b/extras/hs-test/script/nginx_server_entrypoint.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+nginx -c /nginx.conf
diff --git a/extras/hs-test/suite_nginx_test.go b/extras/hs-test/suite_nginx_test.go
deleted file mode 100644
index c559496e71b..00000000000
--- a/extras/hs-test/suite_nginx_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package main
-
-import (
- "reflect"
- "runtime"
- "strings"
- "time"
-
- . "github.com/onsi/ginkgo/v2"
-)
-
-// These correspond to names used in yaml config
-const (
- vppProxyContainerName = "vpp-proxy"
- nginxProxyContainerName = "nginx-proxy"
- nginxServerContainerName = "nginx-server"
- mirroringClientInterfaceName = "hstcln"
- mirroringServerInterfaceName = "hstsrv"
-)
-
-var nginxTests = []func(s *NginxSuite){}
-var nginxSoloTests = []func(s *NginxSuite){}
-
-type NginxSuite struct {
- HstSuite
-}
-
-func registerNginxTests(tests ...func(s *NginxSuite)) {
- nginxTests = append(nginxTests, tests...)
-}
-func registerNginxSoloTests(tests ...func(s *NginxSuite)) {
- nginxSoloTests = append(nginxSoloTests, tests...)
-}
-
-func (s *NginxSuite) SetupSuite() {
- s.HstSuite.SetupSuite()
- s.loadNetworkTopology("2taps")
- s.loadContainerTopology("nginxProxyAndServer")
-}
-
-func (s *NginxSuite) SetupTest() {
- s.HstSuite.SetupTest()
-
- // Setup test conditions
- var sessionConfig Stanza
- sessionConfig.
- newStanza("session").
- append("enable").
- append("use-app-socket-api").close()
-
- cpus := s.AllocateCpus()
- // ... for proxy
- vppProxyContainer := s.getContainerByName(vppProxyContainerName)
- proxyVpp, _ := vppProxyContainer.newVppInstance(cpus, sessionConfig)
- s.assertNil(proxyVpp.start())
-
- clientInterface := s.getInterfaceByName(mirroringClientInterfaceName)
- s.assertNil(proxyVpp.createTap(clientInterface, 1))
-
- serverInterface := s.getInterfaceByName(mirroringServerInterfaceName)
- s.assertNil(proxyVpp.createTap(serverInterface, 2))
-
- nginxContainer := s.getTransientContainerByName(nginxProxyContainerName)
- nginxContainer.create()
-
- values := struct {
- Proxy string
- Server string
- }{
- Proxy: clientInterface.peer.ip4AddressString(),
- Server: serverInterface.ip4AddressString(),
- }
- nginxContainer.createConfig(
- "/nginx.conf",
- "./resources/nginx/nginx_proxy_mirroring.conf",
- values,
- )
- s.assertNil(nginxContainer.start())
-
- proxyVpp.waitForApp("nginx-", 5)
-}
-
-var _ = Describe("NginxSuite", Ordered, ContinueOnFailure, func() {
- var s NginxSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
- for _, test := range nginxTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
-
-var _ = Describe("NginxSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
- var s NginxSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range nginxSoloTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, Label("SOLO"), func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
diff --git a/extras/hs-test/suite_no_topo_test.go b/extras/hs-test/suite_no_topo_test.go
deleted file mode 100644
index 625dca9f3cf..00000000000
--- a/extras/hs-test/suite_no_topo_test.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package main
-
-import (
- "reflect"
- "runtime"
- "strings"
- "time"
-
- . "github.com/onsi/ginkgo/v2"
-)
-
-const (
- singleTopoContainerVpp = "vpp"
- singleTopoContainerNginx = "nginx"
- tapInterfaceName = "htaphost"
-)
-
-var noTopoTests = []func(s *NoTopoSuite){}
-var noTopoSoloTests = []func(s *NoTopoSuite){}
-
-type NoTopoSuite struct {
- HstSuite
-}
-
-func registerNoTopoTests(tests ...func(s *NoTopoSuite)) {
- noTopoTests = append(noTopoTests, tests...)
-}
-func registerNoTopoSoloTests(tests ...func(s *NoTopoSuite)) {
- noTopoSoloTests = append(noTopoSoloTests, tests...)
-}
-
-func (s *NoTopoSuite) SetupSuite() {
- s.HstSuite.SetupSuite()
- s.loadNetworkTopology("tap")
- s.loadContainerTopology("single")
-}
-
-func (s *NoTopoSuite) SetupTest() {
- s.HstSuite.SetupTest()
-
- // Setup test conditions
- var sessionConfig Stanza
- sessionConfig.
- newStanza("session").
- append("enable").
- append("use-app-socket-api").close()
-
- cpus := s.AllocateCpus()
- container := s.getContainerByName(singleTopoContainerVpp)
- vpp, _ := container.newVppInstance(cpus, sessionConfig)
- s.assertNil(vpp.start())
-
- tapInterface := s.getInterfaceByName(tapInterfaceName)
-
- s.assertNil(vpp.createTap(tapInterface), "failed to create tap interface")
-}
-
-var _ = Describe("NoTopoSuite", Ordered, ContinueOnFailure, func() {
- var s NoTopoSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range noTopoTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
-
-var _ = Describe("NoTopoSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
- var s NoTopoSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range noTopoSoloTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, Label("SOLO"), func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
diff --git a/extras/hs-test/suite_ns_test.go b/extras/hs-test/suite_ns_test.go
deleted file mode 100644
index 85b90911c2f..00000000000
--- a/extras/hs-test/suite_ns_test.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package main
-
-import (
- "fmt"
- "reflect"
- "runtime"
- "strings"
- "time"
-
- . "github.com/onsi/ginkgo/v2"
-)
-
-// These correspond to names used in yaml config
-const (
- clientInterface = "hclnvpp"
- serverInterface = "hsrvvpp"
-)
-
-var nsTests = []func(s *NsSuite){}
-var nsSoloTests = []func(s *NsSuite){}
-
-type NsSuite struct {
- HstSuite
-}
-
-func registerNsTests(tests ...func(s *NsSuite)) {
- nsTests = append(nsTests, tests...)
-}
-func registerNsSoloTests(tests ...func(s *NsSuite)) {
- nsSoloTests = append(nsSoloTests, tests...)
-}
-
-func (s *NsSuite) SetupSuite() {
- s.HstSuite.SetupSuite()
- s.configureNetworkTopology("ns")
- s.loadContainerTopology("ns")
-}
-
-func (s *NsSuite) SetupTest() {
- s.HstSuite.SetupTest()
-
- // Setup test conditions
- var sessionConfig Stanza
- sessionConfig.
- newStanza("session").
- append("enable").
- append("use-app-socket-api").
- append("evt_qs_memfd_seg").
- append("event-queue-length 100000").close()
-
- cpus := s.AllocateCpus()
- container := s.getContainerByName("vpp")
- vpp, _ := container.newVppInstance(cpus, sessionConfig)
- s.assertNil(vpp.start())
-
- idx, err := vpp.createAfPacket(s.getInterfaceByName(serverInterface))
- s.assertNil(err, fmt.Sprint(err))
- s.assertNotEqual(0, idx)
-
- idx, err = vpp.createAfPacket(s.getInterfaceByName(clientInterface))
- s.assertNil(err, fmt.Sprint(err))
- s.assertNotEqual(0, idx)
-
- container.exec("chmod 777 -R %s", container.getContainerWorkDir())
-}
-
-var _ = Describe("NsSuite", Ordered, ContinueOnFailure, func() {
- var s NsSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range nsTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
-
-var _ = Describe("NsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
- var s NsSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range nsSoloTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, Label("SOLO"), func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
diff --git a/extras/hs-test/suite_tap_test.go b/extras/hs-test/suite_tap_test.go
deleted file mode 100644
index ebf0f9b3cbc..00000000000
--- a/extras/hs-test/suite_tap_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package main
-
-import (
- "reflect"
- "runtime"
- "strings"
- "time"
-
- . "github.com/onsi/ginkgo/v2"
-)
-
-type TapSuite struct {
- HstSuite
-}
-
-var tapTests = []func(s *TapSuite){}
-var tapSoloTests = []func(s *TapSuite){}
-
-func registerTapTests(tests ...func(s *TapSuite)) {
- tapTests = append(tapTests, tests...)
-}
-func registerTapSoloTests(tests ...func(s *TapSuite)) {
- tapSoloTests = append(tapSoloTests, tests...)
-}
-
-func (s *TapSuite) SetupSuite() {
- time.Sleep(1 * time.Second)
- s.HstSuite.SetupSuite()
- s.configureNetworkTopology("tap")
-}
-
-var _ = Describe("TapSuite", Ordered, ContinueOnFailure, func() {
- var s TapSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range tapTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
-
-var _ = Describe("TapSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
- var s TapSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- for _, test := range tapSoloTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, Label("SOLO"), func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
diff --git a/extras/hs-test/suite_veth_test.go b/extras/hs-test/suite_veth_test.go
deleted file mode 100644
index d47bf8c52a9..00000000000
--- a/extras/hs-test/suite_veth_test.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package main
-
-import (
- "fmt"
- "reflect"
- "runtime"
- "strings"
- "time"
-
- . "github.com/onsi/ginkgo/v2"
-)
-
-// These correspond to names used in yaml config
-const (
- serverInterfaceName = "srv"
- clientInterfaceName = "cln"
-)
-
-var vethTests = []func(s *VethsSuite){}
-var vethSoloTests = []func(s *VethsSuite){}
-
-type VethsSuite struct {
- HstSuite
-}
-
-func registerVethTests(tests ...func(s *VethsSuite)) {
- vethTests = append(vethTests, tests...)
-}
-func registerSoloVethTests(tests ...func(s *VethsSuite)) {
- vethSoloTests = append(vethSoloTests, tests...)
-}
-
-func (s *VethsSuite) SetupSuite() {
- time.Sleep(1 * time.Second)
- s.HstSuite.SetupSuite()
- s.configureNetworkTopology("2peerVeth")
- s.loadContainerTopology("2peerVeth")
-}
-
-func (s *VethsSuite) SetupTest() {
- s.HstSuite.SetupTest()
-
- // Setup test conditions
- var sessionConfig Stanza
- sessionConfig.
- newStanza("session").
- append("enable").
- append("use-app-socket-api").close()
-
- // ... For server
- serverContainer := s.getContainerByName("server-vpp")
-
- cpus := s.AllocateCpus()
- serverVpp, err := serverContainer.newVppInstance(cpus, sessionConfig)
- s.assertNotNil(serverVpp, fmt.Sprint(err))
-
- s.setupServerVpp()
-
- // ... For client
- clientContainer := s.getContainerByName("client-vpp")
-
- cpus = s.AllocateCpus()
- clientVpp, err := clientContainer.newVppInstance(cpus, sessionConfig)
- s.assertNotNil(clientVpp, fmt.Sprint(err))
-
- s.setupClientVpp()
-}
-
-func (s *VethsSuite) setupServerVpp() {
- serverVpp := s.getContainerByName("server-vpp").vppInstance
- s.assertNil(serverVpp.start())
-
- serverVeth := s.getInterfaceByName(serverInterfaceName)
- idx, err := serverVpp.createAfPacket(serverVeth)
- s.assertNil(err, fmt.Sprint(err))
- s.assertNotEqual(0, idx)
-}
-
-func (s *VethsSuite) setupClientVpp() {
- clientVpp := s.getContainerByName("client-vpp").vppInstance
- s.assertNil(clientVpp.start())
-
- clientVeth := s.getInterfaceByName(clientInterfaceName)
- idx, err := clientVpp.createAfPacket(clientVeth)
- s.assertNil(err, fmt.Sprint(err))
- s.assertNotEqual(0, idx)
-}
-
-var _ = Describe("VethsSuite", Ordered, ContinueOnFailure, func() {
- var s VethsSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
-
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- // https://onsi.github.io/ginkgo/#dynamically-generating-specs
- for _, test := range vethTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
-
-var _ = Describe("VethsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
- var s VethsSuite
- BeforeAll(func() {
- s.SetupSuite()
- })
- BeforeEach(func() {
- s.SetupTest()
- })
- AfterAll(func() {
- s.TearDownSuite()
- })
- AfterEach(func() {
- s.TearDownTest()
- })
-
- // https://onsi.github.io/ginkgo/#dynamically-generating-specs
- for _, test := range vethSoloTests {
- test := test
- pc := reflect.ValueOf(test).Pointer()
- funcValue := runtime.FuncForPC(pc)
- testName := strings.Split(funcValue.Name(), ".")[2]
- It(testName, Label("SOLO"), func(ctx SpecContext) {
- s.log(testName + ": BEGIN")
- test(&s)
- }, SpecTimeout(time.Minute*5))
- }
-})
diff --git a/extras/hs-test/test b/extras/hs-test/test
deleted file mode 100755
index fd17feb2c50..00000000000
--- a/extras/hs-test/test
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env bash
-
-source vars
-
-args=
-single_test=0
-persist_set=0
-unconfigure_set=0
-debug_set=0
-vppsrc=
-ginkgo_args=
-parallel=
-
-for i in "$@"
-do
-case "${i}" in
- --persist=*)
- persist="${i#*=}"
- if [ $persist = "true" ]; then
- args="$args -persist"
- persist_set=1
- fi
- ;;
- --debug=*)
- debug="${i#*=}"
- if [ $debug = "true" ]; then
- args="$args -debug"
- debug_set=1
- fi
- ;;
- --verbose=*)
- verbose="${i#*=}"
- if [ $verbose = "true" ]; then
- args="$args -verbose"
- fi
- ;;
- --unconfigure=*)
- unconfigure="${i#*=}"
- if [ $unconfigure = "true" ]; then
- args="$args -unconfigure"
- unconfigure_set=1
- fi
- ;;
- --cpus=*)
- args="$args -cpus ${i#*=}"
- ;;
- --vppsrc=*)
- args="$args -vppsrc ${i#*=}"
- ;;
- --test=*)
- tc_name="${i#*=}"
- if [ $tc_name != "all" ]; then
- single_test=1
- ginkgo_args="$ginkgo_args --focus $tc_name -vv"
- args="$args -verbose"
- else
- ginkgo_args="$ginkgo_args -v"
- fi
- ;;
- --parallel=*)
- ginkgo_args="$ginkgo_args -procs=${i#*=}"
- ;;
- --repeat=*)
- ginkgo_args="$ginkgo_args --repeat=${i#*=}"
- ;;
-esac
-done
-
-if [ $single_test -eq 0 ] && [ $persist_set -eq 1 ]; then
- echo "persist flag is not supported while running all tests!"
- exit 1
-fi
-
-if [ $unconfigure_set -eq 1 ] && [ $single_test -eq 0 ]; then
- echo "a single test has to be specified when unconfigure is set"
- exit 1
-fi
-
-if [ $persist_set -eq 1 ] && [ $unconfigure_set -eq 1 ]; then
- echo "setting persist flag and unconfigure flag is not allowed"
- exit 1
-fi
-
-if [ $single_test -eq 0 ] && [ $debug_set -eq 1 ]; then
- echo "VPP debug flag is not supperted while running all tests!"
- exit 1
-fi
-
-mkdir -p summary
-
-sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --no-color --trace --json-report=summary/report.json $ginkgo_args -- $args
diff --git a/extras/hs-test/tools/http_server/http_server.go b/extras/hs-test/tools/http_server/http_server.go
index d2ab3851643..a65aa7556e7 100644
--- a/extras/hs-test/tools/http_server/http_server.go
+++ b/extras/hs-test/tools/http_server/http_server.go
@@ -8,13 +8,13 @@ import (
)
func main() {
- if len(os.Args) < 3 {
+ if len(os.Args) < 4 {
fmt.Println("arg expected")
os.Exit(1)
}
http.HandleFunc("/httpTestFile", func(w http.ResponseWriter, r *http.Request) {
- file, _ := os.Open("httpTestFile" + os.Args[2])
+ file, _ := os.Open(os.Args[3] + "httpTestFile" + os.Args[2])
defer file.Close()
io.Copy(w, file)
})
diff --git a/extras/hs-test/topo-containers/2containers.yaml b/extras/hs-test/topo-containers/2containers.yaml
new file mode 100644
index 00000000000..1217c27d3ed
--- /dev/null
+++ b/extras/hs-test/topo-containers/2containers.yaml
@@ -0,0 +1,4 @@
+---
+containers:
+ - name: "server"
+ - name: "client" \ No newline at end of file
diff --git a/extras/hs-test/topo-containers/2peerVethLdp.yaml b/extras/hs-test/topo-containers/2peerVethLdp.yaml
new file mode 100644
index 00000000000..bd6e63a945d
--- /dev/null
+++ b/extras/hs-test/topo-containers/2peerVethLdp.yaml
@@ -0,0 +1,18 @@
+---
+volumes:
+ - volume: &server-vol
+ host-dir: "$HST_VOLUME_DIR/server-share"
+ container-dir: "/tmp/server-share"
+ is-default-work-dir: true
+ - volume: &client-vol
+ host-dir: "$HST_VOLUME_DIR/client-share"
+ container-dir: "/tmp/client-share"
+ is-default-work-dir: true
+
+containers:
+ - name: "server-vpp"
+ volumes:
+ - <<: *server-vol
+ - name: "client-vpp"
+ volumes:
+ - <<: *client-vol
diff --git a/extras/hs-test/topo-containers/envoyProxy.yaml b/extras/hs-test/topo-containers/envoyProxy.yaml
new file mode 100644
index 00000000000..92dd9b93c47
--- /dev/null
+++ b/extras/hs-test/topo-containers/envoyProxy.yaml
@@ -0,0 +1,40 @@
+---
+volumes:
+ - volume: &shared-vol
+ host-dir: "$HST_VOLUME_DIR/shared-vol"
+
+containers:
+ - name: "vpp"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/vpp"
+ is-default-work-dir: true
+ - name: "envoy-vcl"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/vpp-envoy"
+ is-default-work-dir: true
+ - host-dir: "$HST_DIR/resources/envoy"
+ container-dir: "/tmp"
+ vars:
+ - name: "ENVOY_UID"
+ value: "0"
+ - name: "VCL_CONFIG"
+ value: "/tmp/vcl.conf"
+ image: "envoyproxy/envoy-contrib:v1.30-latest"
+ extra-args: "--log-format [%t][%l][%g:%#]%_ --concurrency 2 -c /etc/envoy/envoy.yaml"
+ is-optional: true
+ - name: "nginx-server"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/nginx"
+ is-default-work-dir: true
+ image: "hs-test/nginx-server"
+ is-optional: true
+ - name: "curl"
+ vars:
+ - name: LD_LIBRARY_PATH
+ value: "/usr/local/lib"
+ image: "hs-test/curl"
+ is-optional: true
+ run-detached: false
diff --git a/extras/hs-test/topo-containers/nginxProxy.yaml b/extras/hs-test/topo-containers/nginxProxy.yaml
new file mode 100644
index 00000000000..d9ddc14590f
--- /dev/null
+++ b/extras/hs-test/topo-containers/nginxProxy.yaml
@@ -0,0 +1,32 @@
+---
+volumes:
+ - volume: &shared-vol-nginx-proxy
+ host-dir: "$HST_VOLUME_DIR/shared-vol-nginx-proxy"
+
+containers:
+ - name: "vpp"
+ volumes:
+ - <<: *shared-vol-nginx-proxy
+ container-dir: "/tmp/vpp"
+ is-default-work-dir: true
+ - name: "nginx-proxy"
+ volumes:
+ - <<: *shared-vol-nginx-proxy
+ container-dir: "/tmp/nginx"
+ is-default-work-dir: true
+ image: "hs-test/nginx-ldp"
+ is-optional: true
+ - name: "nginx-server"
+ volumes:
+ - <<: *shared-vol-nginx-proxy
+ container-dir: "/tmp/nginx"
+ is-default-work-dir: true
+ image: "hs-test/nginx-server"
+ is-optional: true
+ - name: "curl"
+ vars:
+ - name: LD_LIBRARY_PATH
+ value: "/usr/local/lib"
+ image: "hs-test/curl"
+ is-optional: true
+ run-detached: false
diff --git a/extras/hs-test/topo-containers/ns.yaml b/extras/hs-test/topo-containers/ns.yaml
index 2298ad232c2..3de5af59569 100644
--- a/extras/hs-test/topo-containers/ns.yaml
+++ b/extras/hs-test/topo-containers/ns.yaml
@@ -22,6 +22,6 @@ containers:
value: "0"
- name: "VCL_CONFIG"
value: "/tmp/vcl.conf"
- image: "envoyproxy/envoy-contrib:v1.21-latest"
+ image: "envoyproxy/envoy-contrib:v1.30-latest"
extra-args: "--concurrency 2 -c /etc/envoy/envoy.yaml"
is-optional: true
diff --git a/extras/hs-test/topo-containers/singleCpuPinning.yaml b/extras/hs-test/topo-containers/singleCpuPinning.yaml
new file mode 100644
index 00000000000..6e673aa85bf
--- /dev/null
+++ b/extras/hs-test/topo-containers/singleCpuPinning.yaml
@@ -0,0 +1,11 @@
+---
+volumes:
+ - volume: &shared-vol
+ host-dir: "$HST_VOLUME_DIR/shared-vol"
+
+containers:
+ - name: "vpp"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/vpp"
+ is-default-work-dir: true
diff --git a/extras/hs-test/topo-containers/nginxProxyAndServer.yaml b/extras/hs-test/topo-containers/vppProxy.yaml
index cc6b780bafc..a1f24bbc187 100644
--- a/extras/hs-test/topo-containers/nginxProxyAndServer.yaml
+++ b/extras/hs-test/topo-containers/vppProxy.yaml
@@ -1,20 +1,25 @@
---
volumes:
- - volume: &shared-vol-proxy
- host-dir: "$HST_VOLUME_DIR/shared-vol-proxy"
+ - volume: &shared-vol
+ host-dir: "$HST_VOLUME_DIR/shared-vol"
containers:
- name: "vpp-proxy"
volumes:
- - <<: *shared-vol-proxy
+ - <<: *shared-vol
container-dir: "/tmp/vpp"
is-default-work-dir: true
- - name: "nginx-proxy"
+ - name: "nginx-server"
volumes:
- - <<: *shared-vol-proxy
+ - <<: *shared-vol
container-dir: "/tmp/nginx"
is-default-work-dir: true
- image: "hs-test/nginx-ldp"
- is-optional: true
- - name: "nginx-server"
image: "hs-test/nginx-server"
+ is-optional: true
+ - name: "curl"
+ vars:
+ - name: LD_LIBRARY_PATH
+ value: "/usr/local/lib"
+ image: "hs-test/curl"
+ is-optional: true
+ run-detached: false
diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go
deleted file mode 100644
index 304dd4c241b..00000000000
--- a/extras/hs-test/utils.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package main
-
-import (
- "fmt"
- "io"
- "os"
- "strings"
-)
-
-const networkTopologyDir string = "topo-network/"
-const containerTopologyDir string = "topo-containers/"
-
-type Stanza struct {
- content string
- pad int
-}
-
-type ActionResult struct {
- Err error
- Desc string
- ErrOutput string
- StdOutput string
-}
-
-type JsonResult struct {
- Code int
- Desc string
- ErrOutput string
- StdOutput string
-}
-
-func assertFileSize(f1, f2 string) error {
- fi1, err := os.Stat(f1)
- if err != nil {
- return err
- }
-
- fi2, err1 := os.Stat(f2)
- if err1 != nil {
- return err1
- }
-
- if fi1.Size() != fi2.Size() {
- return fmt.Errorf("file sizes differ (%d vs %d)", fi1.Size(), fi2.Size())
- }
- return nil
-}
-
-func (c *Stanza) newStanza(name string) *Stanza {
- c.append("\n" + name + " {")
- c.pad += 2
- return c
-}
-
-func (c *Stanza) append(name string) *Stanza {
- c.content += strings.Repeat(" ", c.pad)
- c.content += name + "\n"
- return c
-}
-
-func (c *Stanza) close() *Stanza {
- c.content += "}\n"
- c.pad -= 2
- return c
-}
-
-func (s *Stanza) toString() string {
- return s.content
-}
-
-func (s *Stanza) saveToFile(fileName string) error {
- fo, err := os.Create(fileName)
- if err != nil {
- return err
- }
- defer fo.Close()
-
- _, err = io.Copy(fo, strings.NewReader(s.content))
- return err
-}
diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go
index fdcd60ad503..81da0c533b9 100644
--- a/extras/hs-test/vcl_test.go
+++ b/extras/hs-test/vcl_test.go
@@ -3,11 +3,13 @@ package main
import (
"fmt"
"time"
+
+ . "fd.io/hs-test/infra"
)
func init() {
- registerVethTests(XEchoVclClientUdpTest, XEchoVclClientTcpTest, XEchoVclServerUdpTest,
- XEchoVclServerTcpTest, VclEchoTcpTest, VclEchoUdpTest, VclRetryAttachTest)
+ RegisterVethTests(XEchoVclClientUdpTest, XEchoVclClientTcpTest, XEchoVclServerUdpTest,
+ XEchoVclServerTcpTest, VclEchoTcpTest, VclEchoUdpTest, VclHttpPostTest, VclRetryAttachTest)
}
func getVclConfig(c *Container, ns_id_optional ...string) string {
@@ -16,141 +18,145 @@ func getVclConfig(c *Container, ns_id_optional ...string) string {
if len(ns_id_optional) > 0 {
ns_id = ns_id_optional[0]
}
- s.newStanza("vcl").
- append(fmt.Sprintf("app-socket-api %[1]s/var/run/app_ns_sockets/%[2]s", c.getContainerWorkDir(), ns_id)).
- append("app-scope-global").
- append("app-scope-local").
- append("use-mq-eventfd")
+ s.NewStanza("vcl").
+ Append(fmt.Sprintf("app-socket-api %[1]s/var/run/app_ns_sockets/%[2]s", c.GetContainerWorkDir(), ns_id)).
+ Append("app-scope-global").
+ Append("app-scope-local").
+ Append("use-mq-eventfd")
if len(ns_id_optional) > 0 {
- s.append(fmt.Sprintf("namespace-id %[1]s", ns_id)).
- append(fmt.Sprintf("namespace-secret %[1]s", ns_id))
+ s.Append(fmt.Sprintf("namespace-id %[1]s", ns_id)).
+ Append(fmt.Sprintf("namespace-secret %[1]s", ns_id))
}
- return s.close().toString()
+ return s.Close().ToString()
}
func XEchoVclClientUdpTest(s *VethsSuite) {
- s.testXEchoVclClient("udp")
+ testXEchoVclClient(s, "udp")
}
func XEchoVclClientTcpTest(s *VethsSuite) {
- s.testXEchoVclClient("tcp")
+ testXEchoVclClient(s, "tcp")
}
-func (s *VethsSuite) testXEchoVclClient(proto string) {
+func testXEchoVclClient(s *VethsSuite, proto string) {
port := "12345"
- serverVpp := s.getContainerByName("server-vpp").vppInstance
+ serverVpp := s.GetContainerByName("server-vpp").VppInstance
- serverVeth := s.getInterfaceByName(serverInterfaceName)
- serverVpp.vppctl("test echo server uri %s://%s/%s fifo-size 64k", proto, serverVeth.ip4AddressString(), port)
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ serverVpp.Vppctl("test echo server uri %s://%s/%s fifo-size 64k", proto, serverVeth.Ip4AddressString(), port)
- echoClnContainer := s.getTransientContainerByName("client-app")
- echoClnContainer.createFile("/vcl.conf", getVclConfig(echoClnContainer))
+ echoClnContainer := s.GetTransientContainerByName("client-app")
+ echoClnContainer.CreateFile("/vcl.conf", getVclConfig(echoClnContainer))
- testClientCommand := "vcl_test_client -N 100 -p " + proto + " " + serverVeth.ip4AddressString() + " " + port
- s.log(testClientCommand)
- echoClnContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
- o := echoClnContainer.exec(testClientCommand)
- s.log(o)
- s.assertContains(o, "CLIENT RESULTS")
+ testClientCommand := "vcl_test_client -N 100 -p " + proto + " " + serverVeth.Ip4AddressString() + " " + port
+ s.Log(testClientCommand)
+ echoClnContainer.AddEnvVar("VCL_CONFIG", "/vcl.conf")
+ o := echoClnContainer.Exec(testClientCommand)
+ s.Log(o)
+ s.AssertContains(o, "CLIENT RESULTS")
}
func XEchoVclServerUdpTest(s *VethsSuite) {
- s.testXEchoVclServer("udp")
+ testXEchoVclServer(s, "udp")
}
func XEchoVclServerTcpTest(s *VethsSuite) {
- s.testXEchoVclServer("tcp")
+ testXEchoVclServer(s, "tcp")
}
-func (s *VethsSuite) testXEchoVclServer(proto string) {
+func testXEchoVclServer(s *VethsSuite, proto string) {
port := "12345"
- srvVppCont := s.getContainerByName("server-vpp")
- srvAppCont := s.getContainerByName("server-app")
+ srvVppCont := s.GetContainerByName("server-vpp")
+ srvAppCont := s.GetContainerByName("server-app")
- srvAppCont.createFile("/vcl.conf", getVclConfig(srvVppCont))
- srvAppCont.addEnvVar("VCL_CONFIG", "/vcl.conf")
+ srvAppCont.CreateFile("/vcl.conf", getVclConfig(srvVppCont))
+ srvAppCont.AddEnvVar("VCL_CONFIG", "/vcl.conf")
vclSrvCmd := fmt.Sprintf("vcl_test_server -p %s %s", proto, port)
- srvAppCont.execServer(vclSrvCmd)
+ srvAppCont.ExecServer(vclSrvCmd)
- serverVeth := s.getInterfaceByName(serverInterfaceName)
- serverVethAddress := serverVeth.ip4AddressString()
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ serverVethAddress := serverVeth.Ip4AddressString()
- clientVpp := s.getContainerByName("client-vpp").vppInstance
- o := clientVpp.vppctl("test echo client uri %s://%s/%s fifo-size 64k verbose mbytes 2", proto, serverVethAddress, port)
- s.log(o)
- s.assertContains(o, "Test finished at")
+ clientVpp := s.GetContainerByName("client-vpp").VppInstance
+ o := clientVpp.Vppctl("test echo client uri %s://%s/%s fifo-size 64k verbose mbytes 2", proto, serverVethAddress, port)
+ s.Log(o)
+ s.AssertContains(o, "Test finished at")
}
-func (s *VethsSuite) testVclEcho(proto string) {
+func testVclEcho(s *VethsSuite, proto string) {
port := "12345"
- srvVppCont := s.getContainerByName("server-vpp")
- srvAppCont := s.getContainerByName("server-app")
+ srvVppCont := s.GetContainerByName("server-vpp")
+ srvAppCont := s.GetContainerByName("server-app")
- srvAppCont.createFile("/vcl.conf", getVclConfig(srvVppCont))
- srvAppCont.addEnvVar("VCL_CONFIG", "/vcl.conf")
- srvAppCont.execServer("vcl_test_server " + port)
+ srvAppCont.CreateFile("/vcl.conf", getVclConfig(srvVppCont))
+ srvAppCont.AddEnvVar("VCL_CONFIG", "/vcl.conf")
+ srvAppCont.ExecServer("vcl_test_server -p " + proto + " " + port)
- serverVeth := s.getInterfaceByName(serverInterfaceName)
- serverVethAddress := serverVeth.ip4AddressString()
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ serverVethAddress := serverVeth.Ip4AddressString()
- echoClnContainer := s.getTransientContainerByName("client-app")
- echoClnContainer.createFile("/vcl.conf", getVclConfig(echoClnContainer))
+ echoClnContainer := s.GetTransientContainerByName("client-app")
+ echoClnContainer.CreateFile("/vcl.conf", getVclConfig(echoClnContainer))
testClientCommand := "vcl_test_client -p " + proto + " " + serverVethAddress + " " + port
- echoClnContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
- o := echoClnContainer.exec(testClientCommand)
- s.log(o)
+ echoClnContainer.AddEnvVar("VCL_CONFIG", "/vcl.conf")
+ o := echoClnContainer.Exec(testClientCommand)
+ s.Log(o)
}
func VclEchoTcpTest(s *VethsSuite) {
- s.testVclEcho("tcp")
+ testVclEcho(s, "tcp")
}
func VclEchoUdpTest(s *VethsSuite) {
- s.testVclEcho("udp")
+ testVclEcho(s, "udp")
+}
+
+func VclHttpPostTest(s *VethsSuite) {
+ testVclEcho(s, "http")
}
func VclRetryAttachTest(s *VethsSuite) {
- s.testRetryAttach("tcp")
+ testRetryAttach(s, "tcp")
}
-func (s *VethsSuite) testRetryAttach(proto string) {
- srvVppContainer := s.getTransientContainerByName("server-vpp")
+func testRetryAttach(s *VethsSuite, proto string) {
+ srvVppContainer := s.GetTransientContainerByName("server-vpp")
- echoSrvContainer := s.getContainerByName("server-app")
+ echoSrvContainer := s.GetContainerByName("server-app")
- echoSrvContainer.createFile("/vcl.conf", getVclConfig(echoSrvContainer))
+ echoSrvContainer.CreateFile("/vcl.conf", getVclConfig(echoSrvContainer))
- echoSrvContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
- echoSrvContainer.execServer("vcl_test_server -p " + proto + " 12346")
+ echoSrvContainer.AddEnvVar("VCL_CONFIG", "/vcl.conf")
+ echoSrvContainer.ExecServer("vcl_test_server -p " + proto + " 12346")
- s.log("This whole test case can take around 3 minutes to run. Please be patient.")
- s.log("... Running first echo client test, before disconnect.")
+ s.Log("This whole test case can take around 3 minutes to run. Please be patient.")
+ s.Log("... Running first echo client test, before disconnect.")
- serverVeth := s.getInterfaceByName(serverInterfaceName)
- serverVethAddress := serverVeth.ip4AddressString()
+ serverVeth := s.GetInterfaceByName(ServerInterfaceName)
+ serverVethAddress := serverVeth.Ip4AddressString()
- echoClnContainer := s.getTransientContainerByName("client-app")
- echoClnContainer.createFile("/vcl.conf", getVclConfig(echoClnContainer))
+ echoClnContainer := s.GetTransientContainerByName("client-app")
+ echoClnContainer.CreateFile("/vcl.conf", getVclConfig(echoClnContainer))
testClientCommand := "vcl_test_client -U -p " + proto + " " + serverVethAddress + " 12346"
- echoClnContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
- o := echoClnContainer.exec(testClientCommand)
- s.log(o)
- s.log("... First test ended. Stopping VPP server now.")
+ echoClnContainer.AddEnvVar("VCL_CONFIG", "/vcl.conf")
+ o := echoClnContainer.Exec(testClientCommand)
+ s.Log(o)
+ s.Log("... First test ended. Stopping VPP server now.")
// Stop server-vpp-instance, start it again and then run vcl-test-client once more
- srvVppContainer.vppInstance.disconnect()
+ srvVppContainer.VppInstance.Disconnect()
stopVppCommand := "/bin/bash -c 'ps -C vpp_main -o pid= | xargs kill -9'"
- srvVppContainer.exec(stopVppCommand)
+ srvVppContainer.Exec(stopVppCommand)
- s.setupServerVpp()
+ s.SetupServerVpp()
- s.log("... VPP server is starting again, so waiting for a bit.")
+ s.Log("... VPP server is starting again, so waiting for a bit.")
time.Sleep(30 * time.Second) // Wait a moment for the re-attachment to happen
- s.log("... Running second echo client test, after disconnect and re-attachment.")
- o = echoClnContainer.exec(testClientCommand)
- s.log(o)
- s.log("Done.")
+ s.Log("... Running second echo client test, after disconnect and re-attachment.")
+ o = echoClnContainer.Exec(testClientCommand)
+ s.Log(o)
+ s.Log("Done.")
}
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
deleted file mode 100644
index 9b400cfcb77..00000000000
--- a/extras/hs-test/vppinstance.go
+++ /dev/null
@@ -1,412 +0,0 @@
-package main
-
-import (
- "fmt"
- "io"
- "os"
- "os/exec"
- "os/signal"
- "strconv"
- "strings"
- "syscall"
- "time"
-
- "github.com/sirupsen/logrus"
- "github.com/edwarnicke/exechelper"
- . "github.com/onsi/ginkgo/v2"
-
- "go.fd.io/govpp"
- "go.fd.io/govpp/api"
- "go.fd.io/govpp/binapi/af_packet"
- interfaces "go.fd.io/govpp/binapi/interface"
- "go.fd.io/govpp/binapi/interface_types"
- "go.fd.io/govpp/binapi/session"
- "go.fd.io/govpp/binapi/tapv2"
- "go.fd.io/govpp/binapi/vpe"
- "go.fd.io/govpp/core"
-)
-
-const vppConfigTemplate = `unix {
- nodaemon
- log %[1]s%[4]s
- full-coredump
- cli-listen %[1]s%[2]s
- runtime-dir %[1]s/var/run
- gid vpp
-}
-
-api-trace {
- on
-}
-
-api-segment {
- gid vpp
-}
-
-socksvr {
- socket-name %[1]s%[3]s
-}
-
-statseg {
- socket-name %[1]s/var/run/vpp/stats.sock
-}
-
-plugins {
- plugin default { disable }
-
- plugin unittest_plugin.so { enable }
- plugin quic_plugin.so { enable }
- plugin af_packet_plugin.so { enable }
- plugin hs_apps_plugin.so { enable }
- plugin http_plugin.so { enable }
- plugin http_static_plugin.so { enable }
- plugin prom_plugin.so { enable }
- plugin tlsopenssl_plugin.so { enable }
- plugin ping_plugin.so { enable }
- plugin nsim_plugin.so { enable }
-}
-
-logging {
- default-log-level debug
- default-syslog-log-level debug
-}
-
-`
-
-const (
- defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
- defaultApiSocketFilePath = "/var/run/vpp/api.sock"
- defaultLogFilePath = "/var/log/vpp/vpp.log"
-)
-
-type VppInstance struct {
- container *Container
- additionalConfig []Stanza
- connection *core.Connection
- apiChannel api.Channel
- cpus []int
-}
-
-func (vpp *VppInstance) getSuite() *HstSuite {
- return vpp.container.suite
-}
-
-func (vpp *VppInstance) getCliSocket() string {
- return fmt.Sprintf("%s%s", vpp.container.getContainerWorkDir(), defaultCliSocketFilePath)
-}
-
-func (vpp *VppInstance) getRunDir() string {
- return vpp.container.getContainerWorkDir() + "/var/run/vpp"
-}
-
-func (vpp *VppInstance) getLogDir() string {
- return vpp.container.getContainerWorkDir() + "/var/log/vpp"
-}
-
-func (vpp *VppInstance) getEtcDir() string {
- return vpp.container.getContainerWorkDir() + "/etc/vpp"
-}
-
-func (vpp *VppInstance) start() error {
- // Replace default logger in govpp with our own
- govppLogger := logrus.New()
- govppLogger.SetOutput(io.MultiWriter(vpp.getSuite().logger.Writer(), GinkgoWriter))
- core.SetLogger(govppLogger)
- // Create folders
- containerWorkDir := vpp.container.getContainerWorkDir()
-
- vpp.container.exec("mkdir --mode=0700 -p " + vpp.getRunDir())
- vpp.container.exec("mkdir --mode=0700 -p " + vpp.getLogDir())
- vpp.container.exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
-
- // Create startup.conf inside the container
- configContent := fmt.Sprintf(
- vppConfigTemplate,
- containerWorkDir,
- defaultCliSocketFilePath,
- defaultApiSocketFilePath,
- defaultLogFilePath,
- )
- configContent += vpp.generateCpuConfig()
- for _, c := range vpp.additionalConfig {
- configContent += c.toString()
- }
- startupFileName := vpp.getEtcDir() + "/startup.conf"
- vpp.container.createFile(startupFileName, configContent)
-
- // create wrapper script for vppctl with proper CLI socket path
- cliContent := "#!/usr/bin/bash\nvppctl -s " + vpp.getRunDir() + "/cli.sock"
- vppcliFileName := "/usr/bin/vppcli"
- vpp.container.createFile(vppcliFileName, cliContent)
- vpp.container.exec("chmod 0755 " + vppcliFileName)
-
- vpp.getSuite().log("starting vpp")
- if *isVppDebug {
- sig := make(chan os.Signal, 1)
- signal.Notify(sig, syscall.SIGQUIT)
- cont := make(chan bool, 1)
- go func() {
- <-sig
- cont <- true
- }()
-
- vpp.container.execServer("su -c \"vpp -c " + startupFileName + " &> /proc/1/fd/1\"")
- fmt.Println("run following command in different terminal:")
- fmt.Println("docker exec -it " + vpp.container.name + " gdb -ex \"attach $(docker exec " + vpp.container.name + " pidof vpp)\"")
- fmt.Println("Afterwards press CTRL+\\ to continue")
- <-cont
- fmt.Println("continuing...")
- } else {
- // Start VPP
- vpp.container.execServer("su -c \"vpp -c " + startupFileName + " &> /proc/1/fd/1\"")
- }
-
- vpp.getSuite().log("connecting to vpp")
- // Connect to VPP and store the connection
- sockAddress := vpp.container.getHostWorkDir() + defaultApiSocketFilePath
- conn, connEv, err := govpp.AsyncConnect(
- sockAddress,
- core.DefaultMaxReconnectAttempts,
- core.DefaultReconnectInterval)
- if err != nil {
- fmt.Println("async connect error: ", err)
- return err
- }
- vpp.connection = conn
-
- // ... wait for Connected event
- e := <-connEv
- if e.State != core.Connected {
- fmt.Println("connecting to VPP failed: ", e.Error)
- }
-
- // ... check compatibility of used messages
- ch, err := conn.NewAPIChannel()
- if err != nil {
- fmt.Println("creating channel failed: ", err)
- return err
- }
- if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
- fmt.Println("compatibility error: ", err)
- return err
- }
- if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
- fmt.Println("compatibility error: ", err)
- return err
- }
- vpp.apiChannel = ch
-
- return nil
-}
-
-func (vpp *VppInstance) vppctl(command string, arguments ...any) string {
- vppCliCommand := fmt.Sprintf(command, arguments...)
- containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
- vpp.container.name, vpp.getCliSocket(), vppCliCommand)
- vpp.getSuite().log(containerExecCommand)
- output, err := exechelper.CombinedOutput(containerExecCommand)
- vpp.getSuite().assertNil(err)
-
- return string(output)
-}
-
-func (vpp *VppInstance) GetSessionStat(stat string) int {
- o := vpp.vppctl("show session stats")
- vpp.getSuite().log(o)
- for _, line := range strings.Split(o, "\n") {
- if strings.Contains(line, stat) {
- tokens := strings.Split(strings.TrimSpace(line), " ")
- val, err := strconv.Atoi(tokens[0])
- if err != nil {
- Fail("failed to parse stat value %s" + fmt.Sprint(err))
- return 0
- }
- return val
- }
- }
- return 0
-}
-
-func (vpp *VppInstance) waitForApp(appName string, timeout int) {
- vpp.getSuite().log("waiting for app " + appName)
- for i := 0; i < timeout; i++ {
- o := vpp.vppctl("show app")
- if strings.Contains(o, appName) {
- return
- }
- time.Sleep(1 * time.Second)
- }
- vpp.getSuite().assertNil(1, "Timeout while waiting for app '%s'", appName)
-}
-
-func (vpp *VppInstance) createAfPacket(
- veth *NetInterface,
-) (interface_types.InterfaceIndex, error) {
- createReq := &af_packet.AfPacketCreateV2{
- UseRandomHwAddr: true,
- HostIfName: veth.Name(),
- }
- if veth.hwAddress != (MacAddress{}) {
- createReq.UseRandomHwAddr = false
- createReq.HwAddr = veth.hwAddress
- }
- createReply := &af_packet.AfPacketCreateV2Reply{}
-
- vpp.getSuite().log("create af-packet interface " + veth.Name())
- if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
- return 0, err
- }
- veth.index = createReply.SwIfIndex
-
- // Set to up
- upReq := &interfaces.SwInterfaceSetFlags{
- SwIfIndex: veth.index,
- Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
- }
- upReply := &interfaces.SwInterfaceSetFlagsReply{}
-
- vpp.getSuite().log("set af-packet interface " + veth.Name() + " up")
- if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
- return 0, err
- }
-
- // Add address
- if veth.addressWithPrefix() == (AddressWithPrefix{}) {
- var err error
- var ip4Address string
- if ip4Address, err = veth.ip4AddrAllocator.NewIp4InterfaceAddress(veth.peer.networkNumber); err == nil {
- veth.ip4Address = ip4Address
- } else {
- return 0, err
- }
- }
- addressReq := &interfaces.SwInterfaceAddDelAddress{
- IsAdd: true,
- SwIfIndex: veth.index,
- Prefix: veth.addressWithPrefix(),
- }
- addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
-
- vpp.getSuite().log("af-packet interface " + veth.Name() + " add address " + veth.ip4Address)
- if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
- return 0, err
- }
-
- return veth.index, nil
-}
-
-func (vpp *VppInstance) addAppNamespace(
- secret uint64,
- ifx interface_types.InterfaceIndex,
- namespaceId string,
-) error {
- req := &session.AppNamespaceAddDelV2{
- Secret: secret,
- SwIfIndex: ifx,
- NamespaceID: namespaceId,
- }
- reply := &session.AppNamespaceAddDelV2Reply{}
-
- vpp.getSuite().log("add app namespace " + namespaceId)
- if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
- return err
- }
-
- sessionReq := &session.SessionEnableDisable{
- IsEnable: true,
- }
- sessionReply := &session.SessionEnableDisableReply{}
-
- vpp.getSuite().log("enable app namespace " + namespaceId)
- if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
- return err
- }
-
- return nil
-}
-
-func (vpp *VppInstance) createTap(
- tap *NetInterface,
- tapId ...uint32,
-) error {
- var id uint32 = 1
- if len(tapId) > 0 {
- id = tapId[0]
- }
- createTapReq := &tapv2.TapCreateV2{
- ID: id,
- HostIfNameSet: true,
- HostIfName: tap.Name(),
- HostIP4PrefixSet: true,
- HostIP4Prefix: tap.ip4AddressWithPrefix(),
- }
- createTapReply := &tapv2.TapCreateV2Reply{}
-
- vpp.getSuite().log("create tap interface " + tap.Name())
- // Create tap interface
- if err := vpp.apiChannel.SendRequest(createTapReq).ReceiveReply(createTapReply); err != nil {
- return err
- }
-
- // Add address
- addAddressReq := &interfaces.SwInterfaceAddDelAddress{
- IsAdd: true,
- SwIfIndex: createTapReply.SwIfIndex,
- Prefix: tap.peer.addressWithPrefix(),
- }
- addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{}
-
- vpp.getSuite().log("tap interface " + tap.Name() + " add address " + tap.peer.ip4Address)
- if err := vpp.apiChannel.SendRequest(addAddressReq).ReceiveReply(addAddressReply); err != nil {
- return err
- }
-
- // Set interface to up
- upReq := &interfaces.SwInterfaceSetFlags{
- SwIfIndex: createTapReply.SwIfIndex,
- Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
- }
- upReply := &interfaces.SwInterfaceSetFlagsReply{}
-
- vpp.getSuite().log("set tap interface " + tap.Name() + " up")
- if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
- return err
- }
-
- return nil
-}
-
-func (vpp *VppInstance) saveLogs() {
- logTarget := vpp.container.getLogDirPath() + "vppinstance-" + vpp.container.name + ".log"
- logSource := vpp.container.getHostWorkDir() + defaultLogFilePath
- cmd := exec.Command("cp", logSource, logTarget)
- vpp.getSuite().log(cmd.String())
- cmd.Run()
-}
-
-func (vpp *VppInstance) disconnect() {
- vpp.connection.Disconnect()
- vpp.apiChannel.Close()
-}
-
-func (vpp *VppInstance) generateCpuConfig() string {
- var c Stanza
- var s string
- if len(vpp.cpus) < 1 {
- return ""
- }
- c.newStanza("cpu").
- append(fmt.Sprintf("main-core %d", vpp.cpus[0]))
- workers := vpp.cpus[1:]
-
- if len(workers) > 0 {
- for i := 0; i < len(workers); i++ {
- if i != 0 {
- s = s + ", "
- }
- s = s + fmt.Sprintf("%d", workers[i])
- }
- c.append(fmt.Sprintf("corelist-workers %s", s))
- }
- return c.close().toString()
-}
diff --git a/extras/scripts/checkstyle.sh b/extras/scripts/checkstyle.sh
index 2b884f5f08b..304beff51d0 100755
--- a/extras/scripts/checkstyle.sh
+++ b/extras/scripts/checkstyle.sh
@@ -19,7 +19,7 @@ CLANG_FORMAT_VER_REGEX='([0-9]+)\.[0-9]+\.[0-9]+'
CLANG_FORMAT_DIFF="/usr/share/clang/clang-format-diff.py"
# TODO: Remove clang-format-${CLANG_FORMAT_VER} from 'make install-deps' when
-# CLANG_FORMAT_VER default value is upgraded
+# CLANG_FORMAT_VER default value is upgraded to 15 after Ubuntu-20.04 is deprecated
CLANG_FORMAT_VER=${CLANG_FORMAT_VER:-11}
GIT_DIFF_ARGS="-U0 --no-color --relative HEAD~1"
GIT_DIFF_EXCLUDE_LIST=(
@@ -30,16 +30,20 @@ CLANG_FORMAT_DIFF_ARGS="-style file -p1"
SUFFIX="-${CLANG_FORMAT_VER}"
# Attempt to find clang-format to confirm Clang version.
-if command -v clang-format${SUFFIX} &> /dev/null;
+if command -v "clang-format${SUFFIX}" &> /dev/null;
then
- CLANG_FORMAT=clang-format${SUFFIX}
-elif command -v clang-format &> /dev/null;
-then
- CLANG_FORMAT=clang-format
+ CLANG_FORMAT="clang-format${SUFFIX}"
+else
+ echo "*******************************************************************"
+ echo "* CHECKSTYLE FAILED"
+ echo "* Could not locate the clang-format${SUFFIX} script"
+ echo "* Run 'make install-deps' to install it"
+ echo "*******************************************************************"
+ exit 1
fi
CLANG_FORMAT_VERSION=$(${CLANG_FORMAT} --version)
-echo $CLANG_FORMAT_VERSION
+echo "$CLANG_FORMAT_VERSION"
# Confirm that Clang is the expected version.
if [[ ! $CLANG_FORMAT_VERSION =~ $CLANG_FORMAT_VER_REGEX ]];
@@ -75,6 +79,7 @@ then
echo "*******************************************************************"
echo "* CHECKSTYLE FAILED"
echo "* Could not locate the clang-format-diff script"
+ echo "* Run 'make install-deps' to install it"
echo "*******************************************************************"
exit 1
fi
diff --git a/extras/strongswan/vpp_sswan/Makefile b/extras/strongswan/vpp_sswan/Makefile
index 254b90b3b09..85fb506f346 100644
--- a/extras/strongswan/vpp_sswan/Makefile
+++ b/extras/strongswan/vpp_sswan/Makefile
@@ -76,8 +76,8 @@ install-swan:
echo "SSWAN not downloaded, please run "make" or "make pull-swan" first." ; \
exit 1 ; \
fi
- cd ${SWANDIR} && make -j$(nproc)
- cd ${SWANDIR} && sudo make install
+ cd ${SWANDIR} && $(MAKE) -j$(nproc)
+ cd ${SWANDIR} && sudo $(MAKE) install
# check if VPP is installed
ifneq ($(shell test "$(shell ldconfig -p | grep vppinfra.so | awk 'NR==1{print $$1;}')" && echo "yes"), yes)
diff --git a/extras/vpp_stats_fs/stats_fs.go b/extras/vpp_stats_fs/stats_fs.go
index 80c15096234..7b0f1ec193f 100644
--- a/extras/vpp_stats_fs/stats_fs.go
+++ b/extras/vpp_stats_fs/stats_fs.go
@@ -132,7 +132,7 @@ func getCounterContent(index uint32, client *statsclient.StatsClient) (content s
return content, fs.OK
}
-//The dirNode structure represents directories
+// The dirNode structure represents directories
type dirNode struct {
fs.Inode
client *statsclient.StatsClient
@@ -175,7 +175,7 @@ func (dn *dirNode) Opendir(ctx context.Context) syscall.Errno {
return status
}
-//The statNode structure represents counters
+// The statNode structure represents counters
type statNode struct {
fs.Inode
client *statsclient.StatsClient
@@ -192,7 +192,7 @@ func (fh *statNode) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.Attr
return 0
}
-//When a file is opened, the correpsonding counter value is dumped and a file handle is created
+// When a file is opened, the correpsonding counter value is dumped and a file handle is created
func (sn *statNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
content, status := getCounterContent(sn.index, sn.client)
if status == syscall.ENOENT {
@@ -220,7 +220,7 @@ func (fh *statFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadRe
return fuse.ReadResultData(fh.data[off:end]), fs.OK
}
-//NewStatsFileSystem creates the fs for the stat segment.
+// NewStatsFileSystem creates the fs for the stat segment.
func NewStatsFileSystem(sc *statsclient.StatsClient) (root fs.InodeEmbedder, err error) {
return &dirNode{client: sc}, nil
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 68d0a4fe64e..d5c7fd1c718 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -113,19 +113,20 @@ endif()
# cross compiling
##############################################################################
-if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
- set(COMPILER_SUFFIX "linux-gnu")
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
- set(COMPILER_SUFFIX "freebsd")
-endif()
-
if(CMAKE_CROSSCOMPILING)
+ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ set(COMPILER_SUFFIX "linux-gnu")
+ elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+ set(COMPILER_SUFFIX "freebsd")
+ endif()
+
set(CMAKE_IGNORE_PATH
/usr/lib/${CMAKE_HOST_SYSTEM_PROCESSOR}-${COMPILER_SUFFIX}/
/usr/lib/${CMAKE_HOST_SYSTEM_PROCESSOR}-${COMPILER_SUFFIX}/lib/
)
-endif()
set(CMAKE_C_COMPILER_TARGET ${CMAKE_SYSTEM_PROCESSOR}-${COMPILER_SUFFIX})
+endif()
+
##############################################################################
# build config
##############################################################################
diff --git a/src/cmake/api.cmake b/src/cmake/api.cmake
index 10e89d77594..999bbf91be1 100644
--- a/src/cmake/api.cmake
+++ b/src/cmake/api.cmake
@@ -11,14 +11,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# Set the CMP0116 policy
+if(POLICY CMP0116)
+ cmake_policy(SET CMP0116 NEW)
+endif()
+
##############################################################################
# API
##############################################################################
function(vpp_generate_api_c_header file)
set (output_name ${CMAKE_CURRENT_BINARY_DIR}/${file}.h)
+ set (dependency_file ${CMAKE_CURRENT_BINARY_DIR}/${file}.d)
get_filename_component(output_dir ${output_name} DIRECTORY)
if(NOT VPP_APIGEN)
- set(VPP_APIGEN ${CMAKE_SOURCE_DIR}/tools/vppapigen/vppapigen)
+ set(VPP_APIGEN ${CMAKE_SOURCE_DIR}/tools/vppapigen/vppapigen)
+ set(VPPAPIGEN_SUBMODULES
+ ${CMAKE_SOURCE_DIR}/tools/vppapigen/vppapigen_c.py
+ ${CMAKE_SOURCE_DIR}/tools/vppapigen/vppapigen_json.py
+ )
endif()
if (VPP_INCLUDE_DIR)
set(includedir "--includedir" ${VPP_INCLUDE_DIR})
@@ -35,16 +45,31 @@ function(vpp_generate_api_c_header file)
"${CMAKE_CURRENT_BINARY_DIR}/${file}_test2.c"
)
+ get_filename_component(barename ${file} NAME)
+
+# Define a variable for common apigen arguments
+set(COMMON_ARGS
+ OUTPUT ${OUTPUT_HEADERS}
+ COMMAND mkdir -p ${output_dir}
+ COMMAND ${PYENV} ${VPP_APIGEN}
+ ARGS ${includedir} --includedir ${CMAKE_SOURCE_DIR} --input ${CMAKE_CURRENT_SOURCE_DIR}/${file} --outputdir ${output_dir} --output ${output_name} -MF ${dependency_file}
+ DEPENDS ${VPP_APIGEN} ${CMAKE_CURRENT_SOURCE_DIR}/${file} ${VPPAPIGEN_SUBMODULES}
+ COMMENT "Generating API header ${output_name}"
+)
+
+if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.20")
add_custom_command (
- OUTPUT ${OUTPUT_HEADERS}
- COMMAND mkdir -p ${output_dir}
- COMMAND ${PYENV} ${VPP_APIGEN}
- ARGS ${includedir} --includedir ${CMAKE_SOURCE_DIR} --input ${CMAKE_CURRENT_SOURCE_DIR}/${file} --outputdir ${output_dir} --output ${output_name}
- DEPENDS ${VPP_APIGEN} ${CMAKE_CURRENT_SOURCE_DIR}/${file}
- COMMENT "Generating API header ${output_name}"
+ ${COMMON_ARGS}
+ DEPFILE ${dependency_file}
)
- get_filename_component(barename ${file} NAME)
+else()
+ message(WARNING "Your CMake version does not support DEPFILE. Consider upgrading to CMake 3.20 or later for improved dependency handling.")
+ add_custom_command (
+ ${COMMON_ARGS}
+ )
+endif()
set(t ${barename}_deps)
+
if (NOT TARGET ${t})
add_custom_target(${t} ALL DEPENDS ${OUTPUT_HEADERS})
add_dependencies(api_headers ${t})
@@ -158,6 +183,7 @@ function(vpp_add_api_files name dir component)
# "vapi". Both by in-source components (e.g. vpp-api/vapi/vapi.c), and
# out-of-tree plugins use #include <vapi/component.api.vapi.h>.
# ${file} contains the subdirectory, so strip it here.
+ file(MAKE_DIRECTORY ${VPP_BINARY_DIR}/vpp-api/vapi)
get_filename_component(name ${file} NAME)
list(APPEND header_files
${file}.h
@@ -175,3 +201,5 @@ endfunction()
add_custom_target(api_headers
DEPENDS vlibmemory_api_headers vnet_api_headers vpp_api_headers vlib_api_headers
)
+add_custom_target(vapi_headers
+)
diff --git a/src/cmake/platform/cn913x.cmake b/src/cmake/platform/cn913x.cmake
new file mode 100644
index 00000000000..8c5cb739f9b
--- /dev/null
+++ b/src/cmake/platform/cn913x.cmake
@@ -0,0 +1,3 @@
+
+set(VPP_PLATFORM_CACHE_LINE_SIZE 64)
+set(VPP_PLATFORM_MARCH_FLAGS -march=armv8-a+crc+crypto)
diff --git a/src/cmake/platform/octeon9.cmake b/src/cmake/platform/octeon9.cmake
new file mode 100644
index 00000000000..46ca7dfa64a
--- /dev/null
+++ b/src/cmake/platform/octeon9.cmake
@@ -0,0 +1,4 @@
+
+set(VPP_PLATFORM_CACHE_LINE_SIZE 128)
+set(VPP_PLATFORM_MARCH_FLAGS -march=armv8.2-a+crc+crypto)
+set(VPP_PLATFORM_BUFFER_ALIGN 128)
diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c
index e52e82fcf28..fbd94761027 100644
--- a/src/plugins/acl/acl.c
+++ b/src/plugins/acl/acl.c
@@ -2845,6 +2845,17 @@ acl_set_aclplugin_interface_fn (vlib_main_t * vm,
} \
} while (0)
+#define vec_validate_macip_acl_rules(v, idx) \
+ do \
+ { \
+ if (vec_len (v) < idx + 1) \
+ { \
+ vec_validate (v, idx); \
+ v[idx].is_permit = 0x1; \
+ } \
+ } \
+ while (0)
+
static clib_error_t *
acl_set_aclplugin_acl_fn (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
@@ -3062,6 +3073,160 @@ acl_show_aclplugin_macip_interface_fn (vlib_main_t * vm,
return error;
}
+static clib_error_t *
+acl_set_aclplugin_macip_acl_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ vl_api_macip_acl_rule_t *rules = 0;
+ int rule_idx = 0;
+ int rv = 0;
+ u32 acl_index = ~0;
+ u32 action = 0;
+ u8 src_mac[6];
+ u8 *tag = 0;
+ u8 mac_mask_all_1[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ ip_prefix_t src_ip;
+
+ unformat_input_t _line_input, *line_input = &_line_input;
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ vec_validate_macip_acl_rules (rules, rule_idx);
+ if (unformat (line_input, "permit"))
+ {
+ rules[rule_idx].is_permit = 1;
+ }
+ else if (unformat (line_input, "deny"))
+ {
+ rules[rule_idx].is_permit = 0;
+ }
+ else if (unformat (line_input, "action %d", &action))
+ {
+ rules[rule_idx].is_permit = action;
+ }
+ else if (unformat (line_input, "ip %U", unformat_ip_prefix, &src_ip))
+ {
+ ip_prefix_encode2 (&src_ip, &rules[rule_idx].src_prefix);
+ }
+ else if (unformat (line_input, "src"))
+ {
+ /* Everything in MACIP is "source" but allow this verbosity */
+ }
+ else if (unformat (line_input, "mac %U", unformat_mac_address, &src_mac))
+ {
+ memcpy (rules[rule_idx].src_mac, &src_mac,
+ sizeof (rules[rule_idx].src_mac));
+ memcpy (rules[rule_idx].src_mac_mask, &mac_mask_all_1,
+ sizeof (rules[rule_idx].src_mac_mask));
+ }
+ else if (unformat (line_input, "mask %U", unformat_mac_address,
+ &src_mac))
+ {
+ memcpy (rules[rule_idx].src_mac_mask, &src_mac,
+ sizeof (rules[rule_idx].src_mac_mask));
+ }
+ else if (unformat (line_input, "tag %s", &tag))
+ ;
+ else if (unformat (line_input, ","))
+ {
+ rule_idx++;
+ }
+ else
+ break;
+ }
+
+ if (!tag)
+ vec_add (tag, "cli", 4);
+
+ rv = macip_acl_add_list (vec_len (rules), rules, &acl_index, tag);
+ vec_free (rules);
+ vec_free (tag);
+
+ unformat_free (line_input);
+ if (rv)
+ return clib_error_return (0, "Failed to set MACIP ACL rule");
+
+ vlib_cli_output (vm, "ACL index:%u", acl_index);
+ return 0;
+}
+
+static clib_error_t *
+acl_macip_delete_aclplugin_acl_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ int rv;
+ u32 macip_acl_index = ~0;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "index %u", &macip_acl_index))
+ {
+ /* operate on this acl index (which must exist) */
+ }
+ else
+ break;
+ }
+
+ if (macip_acl_index == ~0)
+ return (clib_error_return (0, "invalid acl index"));
+
+ rv = macip_acl_del_list (macip_acl_index);
+
+ unformat_free (line_input);
+ if (rv)
+ return (clib_error_return (0, "Failed to delete ACL index"));
+
+ vlib_cli_output (vm, "Deleted ACL index:%u", macip_acl_index);
+ return 0;
+}
+
+static clib_error_t *
+acl_set_aclplugin_macip_interface_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ int rv = 0;
+ u32 sw_if_index = ~0;
+ u32 acl_index = ~0;
+ u32 is_add = 1;
+ unformat_input_t _line_input, *line_input = &_line_input;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%U", unformat_vnet_sw_interface,
+ vnet_get_main (), &sw_if_index))
+ ;
+ else if (unformat (line_input, "add"))
+ is_add = 1;
+ else if (unformat (line_input, "del"))
+ is_add = 0;
+ else if (unformat (line_input, "acl %u", &acl_index))
+ ;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ return (clib_error_return (0, "invalid interface"));
+
+ if (acl_index == ~0)
+ return (clib_error_return (0, "invalid acl index"));
+
+ rv = macip_acl_interface_add_del_acl (sw_if_index, is_add, acl_index);
+
+ if (rv)
+ return (clib_error_return (0, "Failed to add acl rule to interface"));
+
+ return 0;
+}
+
static void
acl_plugin_show_acl (acl_main_t * am, u32 acl_index)
{
@@ -3632,6 +3797,38 @@ VLIB_CLI_COMMAND (aclplugin_set_acl_command, static) = {
};
/*?
+ * Create an MACIP Access Control List (ACL)
+ * A MACIP ACL is used to add L2-L3 ACL rules.
+ * A MACIP ACL can be added similar to ACL rules by using following command :
+ *
+ * @cliexcmd{set acl-plugin macip acl <permit|deny|action N>
+ * ip <PREFIX> mac <MAC> mask <int> [tag FOO] {use comma
+ * separated list for multiple rules}}
+ ?*/
+VLIB_CLI_COMMAND (aclplugin_macip_set_acl_command, static) = {
+ .path = "set acl-plugin macip acl ",
+ .short_help = "set acl-plugin macip acl <permit|deny|action N> "
+ "ip <PREFIX> mac <MAC> mask <int> [tag FOO] {use comma "
+ "separated list for multiple rules}",
+ .function = acl_set_aclplugin_macip_acl_fn,
+};
+
+/*?
+ * [un]Apply a MACIP ACL to an interface.
+ * The ACL being applied must already exist.
+ *
+ * @cliexpar
+ * <b><em> set acl-plugin macip interface <interface> <acl INDEX> [del]
+ </b></em>
+ * @cliexend
+ ?*/
+VLIB_CLI_COMMAND (aclplugin_macip_set_interface_command, static) = {
+ .path = "set acl-plugin macip interface",
+ .short_help = "set acl-plugin macip interface <interface> <acl INDEX> [del]",
+ .function = acl_set_aclplugin_macip_interface_fn,
+};
+
+/*?
* Delete an Access Control List (ACL)
* Removes an ACL at the specified index, which must exist but not in use by
* any interface.
@@ -3644,6 +3841,20 @@ VLIB_CLI_COMMAND (aclplugin_delete_acl_command, static) = {
.function = acl_delete_aclplugin_acl_fn,
};
+/*?
+ * Delete a MACIP Access Control List (ACL)
+ * Removes an MACIP ACL at the specified index, which must exist but not in
+ * use by
+ * any interface.
+ *
+ * @cliexcmd{delete acl-plugin macip acl index <idx>}
+ ?*/
+VLIB_CLI_COMMAND (aclplugin_macip_delete_acl_command, static) = {
+ .path = "delete acl-plugin macip acl",
+ .short_help = "delete acl-plugin macip acl index <idx>",
+ .function = acl_macip_delete_aclplugin_acl_fn,
+};
+
static clib_error_t *
acl_plugin_config (vlib_main_t * vm, unformat_input_t * input)
{
diff --git a/src/plugins/avf/avf.h b/src/plugins/avf/avf.h
index f6f79cf0e09..774aac0151b 100644
--- a/src/plugins/avf/avf.h
+++ b/src/plugins/avf/avf.h
@@ -180,6 +180,7 @@ typedef struct
u8 int_mode;
u8 buffer_pool_index;
u32 queue_index;
+ u64 total_packets;
} avf_rxq_t;
typedef struct
@@ -198,6 +199,8 @@ typedef struct
avf_tx_desc_t *tmp_descs;
u32 *tmp_bufs;
u32 queue_index;
+ u64 total_packets;
+ u64 no_free_tx_count;
} avf_txq_t;
typedef struct
diff --git a/src/plugins/avf/device.c b/src/plugins/avf/device.c
index 1618800c432..98169f0bcfe 100644
--- a/src/plugins/avf/device.c
+++ b/src/plugins/avf/device.c
@@ -288,6 +288,7 @@ avf_rxq_init (vlib_main_t * vm, avf_device_t * ad, u16 qid, u16 rxq_size)
d->qword[0] = vlib_buffer_get_pa (vm, b);
d++;
}
+ rxq->total_packets = 0;
return 0;
}
@@ -337,6 +338,9 @@ avf_txq_init (vlib_main_t * vm, avf_device_t * ad, u16 qid, u16 txq_size)
vec_validate_aligned (txq->tmp_descs, txq->size, CLIB_CACHE_LINE_BYTES);
vec_validate_aligned (txq->tmp_bufs, txq->size, CLIB_CACHE_LINE_BYTES);
+ txq->total_packets = 0;
+ txq->no_free_tx_count = 0;
+
return 0;
}
diff --git a/src/plugins/avf/format.c b/src/plugins/avf/format.c
index 0a153a093d9..436f5b9fbf2 100644
--- a/src/plugins/avf/format.c
+++ b/src/plugins/avf/format.c
@@ -104,6 +104,7 @@ format_avf_device (u8 * s, va_list * args)
u8 *a = 0;
avf_rxq_t *rxq = vec_elt_at_index (ad->rxqs, 0);
avf_txq_t *txq = vec_elt_at_index (ad->txqs, 0);
+ u32 idx = 0;
s = format (s, "rx: queues %u, desc %u (min %u max %u)", ad->n_rx_queues,
rxq->size, AVF_QUEUE_SZ_MIN, AVF_QUEUE_SZ_MAX);
@@ -114,6 +115,22 @@ format_avf_device (u8 * s, va_list * args)
format_avf_device_flags, ad);
s = format (s, "\n%Ucapability flags: %U", format_white_space, indent,
format_avf_vf_cap_flags, ad->cap_flags);
+ s =
+ format (s, "\n%U Rx Queue: Total Packets", format_white_space, indent + 4);
+ for (idx = 0; idx < ad->n_rx_queues; idx++)
+ {
+ rxq = vec_elt_at_index (ad->rxqs, idx);
+ s = format (s, "\n%U %8u : %llu", format_white_space, indent + 4, idx,
+ rxq->total_packets);
+ }
+ s = format (s, "\n%U Tx Queue: Total Packets\t Total Drops",
+ format_white_space, indent + 4);
+ for (idx = 0; idx < ad->n_tx_queues; idx++)
+ {
+ txq = vec_elt_at_index (ad->txqs, idx);
+ s = format (s, "\n%U %8u : %llu\t %llu", format_white_space, indent + 4,
+ idx, txq->total_packets, txq->no_free_tx_count);
+ }
s = format (s, "\n%Unum-queue-pairs %d max-vectors %u max-mtu %u "
"rss-key-size %u rss-lut-size %u", format_white_space, indent,
diff --git a/src/plugins/avf/input.c b/src/plugins/avf/input.c
index 06007db540d..890259c88ab 100644
--- a/src/plugins/avf/input.c
+++ b/src/plugins/avf/input.c
@@ -539,6 +539,8 @@ done:
else
avf_rxq_refill (vm, node, rxq, 0 /* use_va_dma */ );
+ rxq->total_packets += n_rx_packets;
+
return n_rx_packets;
}
diff --git a/src/plugins/avf/output.c b/src/plugins/avf/output.c
index daa86ae86b2..0952886aaee 100644
--- a/src/plugins/avf/output.c
+++ b/src/plugins/avf/output.c
@@ -510,6 +510,7 @@ retry:
avf_tail_write (txq->qtx_tail, txq->next);
txq->n_enqueued += n_desc;
n_left -= n_enq;
+ txq->total_packets += n_enq;
}
if (n_left)
@@ -522,6 +523,7 @@ retry:
vlib_buffer_free (vm, buffers, n_left);
vlib_error_count (vm, node->node_index,
AVF_TX_ERROR_NO_FREE_SLOTS, n_left);
+ txq->no_free_tx_count += n_left;
}
if (tf->shared_queue)
diff --git a/src/plugins/builtinurl/FEATURE.yaml b/src/plugins/builtinurl/FEATURE.yaml
deleted file mode 100644
index ba8e3c7ea7b..00000000000
--- a/src/plugins/builtinurl/FEATURE.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-name: Builtin URL support for the static http or https server
-maintainer: Dave Barach <dave@barachs.net>
-features:
- - Builtin URLs for the static http/https server
-description: "The (builtinurl) plugin adds a set of URLs to the static http/https server.
- Current URLs, all of which return data in .json fmt:
- <root-url>/version.json - vpp version info
- <root-url>/interface_list.json - list of interfaces
- <root-url>/interface_stats - single interface via HTTP POST
- <root-url>/interface_stats - all intfcs via HTTP GET."
-state: development
-properties: [API, CLI, MULTITHREAD]
diff --git a/src/plugins/builtinurl/builtins.c b/src/plugins/builtinurl/builtins.c
deleted file mode 100644
index b04e9dd5c7c..00000000000
--- a/src/plugins/builtinurl/builtins.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <vnet/vnet.h>
-#include <builtinurl/builtinurl.h>
-#include <http_static/http_static.h>
-#include <vpp/app/version.h>
-
-hss_url_handler_rc_t
-handle_get_version (hss_url_handler_args_t *args)
-{
- u8 *s = 0;
-
- /* Build some json bullshit */
- s = format (s, "{\"vpp_details\": {");
- s = format (s, " \"version\": \"%s\",", VPP_BUILD_VER);
- s = format (s, " \"build_date\": \"%s\"}}\r\n", VPP_BUILD_DATE);
-
- args->data = s;
- args->data_len = vec_len (s);
- args->free_vec_data = 1;
- return HSS_URL_HANDLER_OK;
-}
-
-void
-trim_path_from_request (u8 * s, char *path)
-{
- u8 *cp;
- int trim_length = strlen (path) + 1 /* remove '?' */ ;
-
- /* Get rid of the path and question-mark */
- vec_delete (s, trim_length, 0);
-
- /* Tail trim irrelevant browser info */
- cp = s;
- while ((cp - s) < vec_len (s))
- {
- if (*cp == ' ')
- {
- /*
- * Makes request a vector which happens to look
- * like a c-string.
- */
- *cp = 0;
- vec_set_len (s, cp - s);
- break;
- }
- cp++;
- }
-}
-
-hss_url_handler_rc_t
-handle_get_interface_stats (hss_url_handler_args_t *args)
-{
- u8 *s = 0, *stats = 0;
- uword *p;
- u32 *sw_if_indices = 0;
- vnet_hw_interface_t *hi;
- vnet_sw_interface_t *si;
- char *q = "\"";
- int i;
- int need_comma = 0;
- u8 *format_vnet_sw_interface_cntrs (u8 * s, vnet_interface_main_t * im,
- vnet_sw_interface_t * si, int json);
- vnet_main_t *vnm = vnet_get_main ();
- vnet_interface_main_t *im = &vnm->interface_main;
-
- /* Get stats for a single interface via http POST */
- if (args->reqtype == HTTP_REQ_POST)
- {
- trim_path_from_request (args->request, "interface_stats.json");
-
- /* Find the sw_if_index */
- p = hash_get (im->hw_interface_by_name, args->request);
- if (!p)
- {
- s = format (s, "{\"interface_stats\": {[\n");
- s = format (s, " \"name\": \"%s\",", args->request);
- s = format (s, " \"error\": \"%s\"", "UnknownInterface");
- s = format (s, "]}\n");
- goto out;
- }
-
- vec_add1 (sw_if_indices, p[0]);
- }
- else /* default, HTTP_BUILTIN_METHOD_GET */
- {
- pool_foreach (hi, im->hw_interfaces)
- {
- vec_add1 (sw_if_indices, hi->sw_if_index);
- }
- }
-
- s = format (s, "{%sinterface_stats%s: [\n", q, q);
-
- for (i = 0; i < vec_len (sw_if_indices); i++)
- {
- si = vnet_get_sw_interface (vnm, sw_if_indices[i]);
- if (need_comma)
- s = format (s, ",\n");
-
- need_comma = 1;
-
- s = format (s, "{%sname%s: %s%U%s, ", q, q, q,
- format_vnet_sw_if_index_name, vnm, sw_if_indices[i], q);
-
- stats = format_vnet_sw_interface_cntrs (stats, &vnm->interface_main, si,
- 1 /* want json */ );
- if (vec_len (stats))
- s = format (s, "%v}", stats);
- else
- s = format (s, "%snone%s: %strue%s}", q, q, q, q);
- vec_reset_length (stats);
- }
-
- s = format (s, "]}\n");
-
-out:
- args->data = s;
- args->data_len = vec_len (s);
- args->free_vec_data = 1;
- vec_free (sw_if_indices);
- vec_free (stats);
- return HSS_URL_HANDLER_OK;
-}
-
-hss_url_handler_rc_t
-handle_get_interface_list (hss_url_handler_args_t *args)
-{
- u8 *s = 0;
- int i;
- vnet_main_t *vnm = vnet_get_main ();
- vnet_interface_main_t *im = &vnm->interface_main;
- vnet_hw_interface_t *hi;
- u32 *hw_if_indices = 0;
- int need_comma = 0;
-
- /* Construct vector of active hw_if_indexes ... */
- pool_foreach (hi, im->hw_interfaces)
- {
- /* No point in mentioning "local0"... */
- if (hi - im->hw_interfaces)
- vec_add1 (hw_if_indices, hi - im->hw_interfaces);
- }
-
- /* Build answer */
- s = format (s, "{\"interface_list\": [\n");
- for (i = 0; i < vec_len (hw_if_indices); i++)
- {
- if (need_comma)
- s = format (s, ",\n");
- hi = pool_elt_at_index (im->hw_interfaces, hw_if_indices[i]);
- s = format (s, "\"%v\"", hi->name);
- need_comma = 1;
- }
- s = format (s, "]}\n");
- vec_free (hw_if_indices);
-
- args->data = s;
- args->data_len = vec_len (s);
- args->free_vec_data = 1;
- return HSS_URL_HANDLER_OK;
-}
-
-void
-builtinurl_handler_init (builtinurl_main_t * bm)
-{
-
- bm->register_handler (handle_get_version, "version.json", HTTP_REQ_GET);
- bm->register_handler (handle_get_interface_list, "interface_list.json",
- HTTP_REQ_GET);
- bm->register_handler (handle_get_interface_stats, "interface_stats.json",
- HTTP_REQ_GET);
- bm->register_handler (handle_get_interface_stats, "interface_stats.json",
- HTTP_REQ_POST);
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/builtinurl/builtinurl.api b/src/plugins/builtinurl/builtinurl.api
deleted file mode 100644
index f292fd77a8e..00000000000
--- a/src/plugins/builtinurl/builtinurl.api
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * builtinurl.api - binary API skeleton
- *
- * Copyright (c) <current-year> <your-organization>
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file builtinurl.api
- * @brief VPP control-plane API messages.
- *
- * This file defines VPP control-plane binary API messages which are generally
- * called through a shared memory interface.
- */
-
-/* Version and type recitations */
-
-option version = "1.0.0";
-
-/** @brief API to enable / disable builtinurl on an interface
- @param client_index - opaque cookie to identify the sender
- @param context - sender context, to match reply w/ request
- @param enable_disable - 1 to enable, 0 to disable the feature
- @param sw_if_index - interface handle
-*/
-
-autoreply define builtinurl_enable {
- /* Client identifier, set from api_main.my_client_index */
- u32 client_index;
-
- /* Arbitrary context, so client can match reply to request */
- u32 context;
-};
diff --git a/src/plugins/builtinurl/builtinurl.c b/src/plugins/builtinurl/builtinurl.c
deleted file mode 100644
index 749a2c93b8a..00000000000
--- a/src/plugins/builtinurl/builtinurl.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * builtinurl.c - skeleton vpp engine plug-in
- *
- * Copyright (c) 2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <vnet/vnet.h>
-#include <vnet/plugin/plugin.h>
-#include <builtinurl/builtinurl.h>
-
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-#include <vpp/app/version.h>
-#include <stdbool.h>
-
-/* define message IDs */
-#include <builtinurl/builtinurl.api_enum.h>
-#include <builtinurl/builtinurl.api_types.h>
-
-#define REPLY_MSG_ID_BASE bmp->msg_id_base
-#include <vlibapi/api_helper_macros.h>
-
-builtinurl_main_t builtinurl_main;
-
-/* Action function shared between message handler and debug CLI */
-
-int
-builtinurl_enable (builtinurl_main_t * bmp)
-{
- void (*fp) (void *, char *, int);
-
- if (bmp->initialized)
- return 0;
-
- /* Look up the builtin URL registration handler */
- fp = vlib_get_plugin_symbol
- ("http_static_plugin.so", "http_static_server_register_builtin_handler");
-
- /* Most likely, the http_static plugin isn't loaded. Done. */
- if (fp == 0)
- return VNET_API_ERROR_NO_SUCH_TABLE;
-
- bmp->register_handler = fp;
- builtinurl_handler_init (bmp);
- bmp->initialized = 1;
-
- return 0;
-}
-
-static clib_error_t *
-builtinurl_enable_command_fn (vlib_main_t * vm,
- unformat_input_t * input,
- vlib_cli_command_t * cmd)
-{
- builtinurl_main_t *bmp = &builtinurl_main;
-
- int rv;
-
- rv = builtinurl_enable (bmp);
-
- switch (rv)
- {
- case 0:
- break;
-
- case VNET_API_ERROR_NO_SUCH_TABLE:
- return clib_error_return
- (0, "http_static_server_register_builtin_handler undefined");
- break;
-
- default:
- return clib_error_return (0, "builtinurl_enable returned %d", rv);
- }
- return 0;
-}
-
-VLIB_CLI_COMMAND (builtinurl_enable_command, static) =
-{
- .path = "builtinurl enable",
- .short_help = "Turn on builtin http/https GET and POST urls",
- .function = builtinurl_enable_command_fn,
-};
-
-/* API message handler */
-static void vl_api_builtinurl_enable_t_handler
- (vl_api_builtinurl_enable_t * mp)
-{
- vl_api_builtinurl_enable_reply_t *rmp;
- builtinurl_main_t *bmp = &builtinurl_main;
- int rv;
-
- rv = builtinurl_enable (bmp);
-
- REPLY_MACRO (VL_API_BUILTINURL_ENABLE_REPLY);
-}
-
-#include <builtinurl/builtinurl.api.c>
-static clib_error_t *
-builtinurl_init (vlib_main_t * vm)
-{
- builtinurl_main_t *bmp = &builtinurl_main;
-
- bmp->vlib_main = vm;
- bmp->vnet_main = vnet_get_main ();
-
- /* Ask for a correctly-sized block of API message decode slots */
- bmp->msg_id_base = setup_message_id_table ();
-
- return 0;
-}
-
-VLIB_INIT_FUNCTION (builtinurl_init);
-
-VLIB_PLUGIN_REGISTER () =
-{
- .version = VPP_BUILD_VER,
- .description = "vpp built-in URL support",
-};
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/builtinurl/builtinurl.h b/src/plugins/builtinurl/builtinurl.h
deleted file mode 100644
index 91302c1eee5..00000000000
--- a/src/plugins/builtinurl/builtinurl.h
+++ /dev/null
@@ -1,57 +0,0 @@
-
-/*
- * builtinurl.h - built-in URLs for the http static server
- *
- * Copyright (c) 2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef __included_builtinurl_h__
-#define __included_builtinurl_h__
-
-#include <vnet/vnet.h>
-#include <vnet/ip/ip.h>
-#include <vnet/ethernet/ethernet.h>
-
-#include <vppinfra/hash.h>
-#include <vppinfra/error.h>
-
-typedef struct
-{
- /* API message ID base */
- u16 msg_id_base;
-
- /* GET / POST handler registration function */
- void (*register_handler) (void *, char *, int);
-
- /* Been there, done that */
- int initialized;
-
- /* convenience */
- vlib_main_t *vlib_main;
- vnet_main_t *vnet_main;
- ethernet_main_t *ethernet_main;
-} builtinurl_main_t;
-
-extern builtinurl_main_t builtinurl_main;
-
-void builtinurl_handler_init (builtinurl_main_t * bm);
-
-#endif /* __included_builtinurl_h__ */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/builtinurl/builtinurl_test.c b/src/plugins/builtinurl/builtinurl_test.c
deleted file mode 100644
index 9edfb81c525..00000000000
--- a/src/plugins/builtinurl/builtinurl_test.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * builtinurl.c - skeleton vpp-api-test plug-in
- *
- * Copyright (c) 2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <vat/vat.h>
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-#include <vppinfra/error.h>
-#include <stdbool.h>
-
-uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
-
-/* Declare message IDs */
-#include <builtinurl/builtinurl.api_enum.h>
-#include <builtinurl/builtinurl.api_types.h>
-
-typedef struct
-{
- /* API message ID base */
- u16 msg_id_base;
- vat_main_t *vat_main;
-} builtinurl_test_main_t;
-
-builtinurl_test_main_t builtinurl_test_main;
-
-#define __plugin_msg_base builtinurl_test_main.msg_id_base
-#include <vlibapi/vat_helper_macros.h>
-
-static int
-api_builtinurl_enable (vat_main_t * vam)
-{
- vl_api_builtinurl_enable_t *mp;
- int ret;
-
- /* Construct the API message */
- M (BUILTINURL_ENABLE, mp);
-
- /* send it... */
- S (mp);
-
- /* Wait for a reply... */
- W (ret);
- return ret;
-}
-
-#include <builtinurl/builtinurl.api_test.c>
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/crypto_native/aes_cbc.c b/src/plugins/crypto_native/aes_cbc.c
index dd7ca3f1cf1..c981897783f 100644
--- a/src/plugins/crypto_native/aes_cbc.c
+++ b/src/plugins/crypto_native/aes_cbc.c
@@ -25,191 +25,40 @@
#pragma GCC optimize ("O3")
#endif
-#if defined(__VAES__) && defined(__AVX512F__)
-#define u8xN u8x64
-#define u32xN u32x16
-#define u32xN_min_scalar u32x16_min_scalar
-#define u32xN_is_all_zero u32x16_is_all_zero
-#define u32xN_splat u32x16_splat
-#elif defined(__VAES__)
-#define u8xN u8x32
-#define u32xN u32x8
-#define u32xN_min_scalar u32x8_min_scalar
-#define u32xN_is_all_zero u32x8_is_all_zero
-#define u32xN_splat u32x8_splat
-#else
-#define u8xN u8x16
-#define u32xN u32x4
-#define u32xN_min_scalar u32x4_min_scalar
-#define u32xN_is_all_zero u32x4_is_all_zero
-#define u32xN_splat u32x4_splat
-#endif
+#define CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE 256
static_always_inline u32
aes_ops_enc_aes_cbc (vlib_main_t * vm, vnet_crypto_op_t * ops[],
u32 n_ops, aes_key_size_t ks)
{
crypto_native_main_t *cm = &crypto_native_main;
- int rounds = AES_KEY_ROUNDS (ks);
- u8 placeholder[8192];
- u32 i, j, count, n_left = n_ops;
- u32xN placeholder_mask = { };
- u32xN len = { };
- vnet_crypto_key_index_t key_index[4 * N_AES_LANES];
- u8 *src[4 * N_AES_LANES] = {};
- u8 *dst[4 * N_AES_LANES] = {};
- u8xN r[4] = {};
- u8xN k[15][4] = {};
-
- for (i = 0; i < 4 * N_AES_LANES; i++)
- key_index[i] = ~0;
-
-more:
- for (i = 0; i < 4 * N_AES_LANES; i++)
- if (len[i] == 0)
- {
- if (n_left == 0)
- {
- /* no more work to enqueue, so we are enqueueing placeholder buffer */
- src[i] = dst[i] = placeholder;
- len[i] = sizeof (placeholder);
- placeholder_mask[i] = 0;
- }
- else
- {
- u8x16 t = aes_block_load (ops[0]->iv);
- ((u8x16 *) r)[i] = t;
-
- src[i] = ops[0]->src;
- dst[i] = ops[0]->dst;
- len[i] = ops[0]->len;
- placeholder_mask[i] = ~0;
- if (key_index[i] != ops[0]->key_index)
- {
- aes_cbc_key_data_t *kd;
- key_index[i] = ops[0]->key_index;
- kd = (aes_cbc_key_data_t *) cm->key_data[key_index[i]];
- for (j = 0; j < rounds + 1; j++)
- ((u8x16 *) k[j])[i] = kd->encrypt_key[j];
- }
- ops[0]->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
- n_left--;
- ops++;
- }
- }
-
- count = u32xN_min_scalar (len);
-
- ASSERT (count % 16 == 0);
-
- for (i = 0; i < count; i += 16)
+ u32 i, n_left = n_ops;
+ uword key_indices[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {};
+ u8 *plaintext[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {};
+ uword oplen[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {};
+ u8 *iv[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {};
+ u8 *ciphertext[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {};
+
+ while (n_left)
{
-#if defined(__VAES__) && defined(__AVX512F__)
- r[0] = u8x64_xor3 (r[0], aes_block_load_x4 (src, i), k[0][0]);
- r[1] = u8x64_xor3 (r[1], aes_block_load_x4 (src + 4, i), k[0][1]);
- r[2] = u8x64_xor3 (r[2], aes_block_load_x4 (src + 8, i), k[0][2]);
- r[3] = u8x64_xor3 (r[3], aes_block_load_x4 (src + 12, i), k[0][3]);
-
- for (j = 1; j < rounds; j++)
+ i = 0;
+ while (n_left && i < CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE)
{
- r[0] = aes_enc_round_x4 (r[0], k[j][0]);
- r[1] = aes_enc_round_x4 (r[1], k[j][1]);
- r[2] = aes_enc_round_x4 (r[2], k[j][2]);
- r[3] = aes_enc_round_x4 (r[3], k[j][3]);
+ key_indices[i] = ops[0]->key_index;
+ plaintext[i] = ops[0]->src;
+ ciphertext[i] = ops[0]->dst;
+ oplen[i] = ops[0]->len;
+ iv[i] = ops[0]->iv;
+ ops[0]->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
+
+ ops++;
+ n_left--;
+ i++;
}
- r[0] = aes_enc_last_round_x4 (r[0], k[j][0]);
- r[1] = aes_enc_last_round_x4 (r[1], k[j][1]);
- r[2] = aes_enc_last_round_x4 (r[2], k[j][2]);
- r[3] = aes_enc_last_round_x4 (r[3], k[j][3]);
-
- aes_block_store_x4 (dst, i, r[0]);
- aes_block_store_x4 (dst + 4, i, r[1]);
- aes_block_store_x4 (dst + 8, i, r[2]);
- aes_block_store_x4 (dst + 12, i, r[3]);
-#elif defined(__VAES__)
- r[0] = u8x32_xor3 (r[0], aes_block_load_x2 (src, i), k[0][0]);
- r[1] = u8x32_xor3 (r[1], aes_block_load_x2 (src + 2, i), k[0][1]);
- r[2] = u8x32_xor3 (r[2], aes_block_load_x2 (src + 4, i), k[0][2]);
- r[3] = u8x32_xor3 (r[3], aes_block_load_x2 (src + 6, i), k[0][3]);
-
- for (j = 1; j < rounds; j++)
- {
- r[0] = aes_enc_round_x2 (r[0], k[j][0]);
- r[1] = aes_enc_round_x2 (r[1], k[j][1]);
- r[2] = aes_enc_round_x2 (r[2], k[j][2]);
- r[3] = aes_enc_round_x2 (r[3], k[j][3]);
- }
- r[0] = aes_enc_last_round_x2 (r[0], k[j][0]);
- r[1] = aes_enc_last_round_x2 (r[1], k[j][1]);
- r[2] = aes_enc_last_round_x2 (r[2], k[j][2]);
- r[3] = aes_enc_last_round_x2 (r[3], k[j][3]);
-
- aes_block_store_x2 (dst, i, r[0]);
- aes_block_store_x2 (dst + 2, i, r[1]);
- aes_block_store_x2 (dst + 4, i, r[2]);
- aes_block_store_x2 (dst + 6, i, r[3]);
-#else
-#if __x86_64__
- r[0] = u8x16_xor3 (r[0], aes_block_load (src[0] + i), k[0][0]);
- r[1] = u8x16_xor3 (r[1], aes_block_load (src[1] + i), k[0][1]);
- r[2] = u8x16_xor3 (r[2], aes_block_load (src[2] + i), k[0][2]);
- r[3] = u8x16_xor3 (r[3], aes_block_load (src[3] + i), k[0][3]);
-
- for (j = 1; j < rounds; j++)
- {
- r[0] = aes_enc_round_x1 (r[0], k[j][0]);
- r[1] = aes_enc_round_x1 (r[1], k[j][1]);
- r[2] = aes_enc_round_x1 (r[2], k[j][2]);
- r[3] = aes_enc_round_x1 (r[3], k[j][3]);
- }
-
- r[0] = aes_enc_last_round_x1 (r[0], k[j][0]);
- r[1] = aes_enc_last_round_x1 (r[1], k[j][1]);
- r[2] = aes_enc_last_round_x1 (r[2], k[j][2]);
- r[3] = aes_enc_last_round_x1 (r[3], k[j][3]);
-
- aes_block_store (dst[0] + i, r[0]);
- aes_block_store (dst[1] + i, r[1]);
- aes_block_store (dst[2] + i, r[2]);
- aes_block_store (dst[3] + i, r[3]);
-#else
- r[0] ^= aes_block_load (src[0] + i);
- r[1] ^= aes_block_load (src[1] + i);
- r[2] ^= aes_block_load (src[2] + i);
- r[3] ^= aes_block_load (src[3] + i);
- for (j = 0; j < rounds - 1; j++)
- {
- r[0] = vaesmcq_u8 (vaeseq_u8 (r[0], k[j][0]));
- r[1] = vaesmcq_u8 (vaeseq_u8 (r[1], k[j][1]));
- r[2] = vaesmcq_u8 (vaeseq_u8 (r[2], k[j][2]));
- r[3] = vaesmcq_u8 (vaeseq_u8 (r[3], k[j][3]));
- }
- r[0] = vaeseq_u8 (r[0], k[j][0]) ^ k[rounds][0];
- r[1] = vaeseq_u8 (r[1], k[j][1]) ^ k[rounds][1];
- r[2] = vaeseq_u8 (r[2], k[j][2]) ^ k[rounds][2];
- r[3] = vaeseq_u8 (r[3], k[j][3]) ^ k[rounds][3];
- aes_block_store (dst[0] + i, r[0]);
- aes_block_store (dst[1] + i, r[1]);
- aes_block_store (dst[2] + i, r[2]);
- aes_block_store (dst[3] + i, r[3]);
-#endif
-#endif
+ clib_aes_cbc_encrypt_multi ((aes_cbc_key_data_t **) cm->key_data,
+ key_indices, plaintext, oplen, iv, ks,
+ ciphertext, i);
}
-
- len -= u32xN_splat (count);
-
- for (i = 0; i < 4 * N_AES_LANES; i++)
- {
- src[i] += count;
- dst[i] += count;
- }
-
- if (n_left > 0)
- goto more;
-
- if (!u32xN_is_all_zero (len & placeholder_mask))
- goto more;
-
return n_ops;
}
diff --git a/src/plugins/crypto_native/sha2.c b/src/plugins/crypto_native/sha2.c
index 459ce6d8e79..6787f629104 100644
--- a/src/plugins/crypto_native/sha2.c
+++ b/src/plugins/crypto_native/sha2.c
@@ -118,13 +118,25 @@ sha2_key_add (vnet_crypto_key_t *key, clib_sha2_type_t type)
static int
probe ()
{
-#if defined(__SHA__) && defined(__x86_64__)
+#if defined(__x86_64__)
+
+#if defined(__SHA__) && defined(__AVX512F__)
+ if (clib_cpu_supports_sha () && clib_cpu_supports_avx512f ())
+ return 30;
+#elif defined(__SHA__) && defined(__AVX2__)
+ if (clib_cpu_supports_sha () && clib_cpu_supports_avx2 ())
+ return 20;
+#elif defined(__SHA__)
if (clib_cpu_supports_sha ())
- return 50;
-#elif defined(__ARM_FEATURE_SHA2)
+ return 10;
+#endif
+
+#elif defined(__aarch64__)
+#if defined(__ARM_FEATURE_SHA2)
if (clib_cpu_supports_sha2 ())
return 10;
#endif
+#endif
return -1;
}
diff --git a/src/plugins/crypto_openssl/main.c b/src/plugins/crypto_openssl/main.c
index b070cf336a5..c59b5d34a29 100644
--- a/src/plugins/crypto_openssl/main.c
+++ b/src/plugins/crypto_openssl/main.c
@@ -219,6 +219,17 @@ openssl_ops_enc_aead (vlib_main_t *vm, vnet_crypto_op_t *ops[],
vnet_crypto_op_t *op = ops[i];
int len = 0;
+ if (i + 2 < n_ops)
+ {
+ CLIB_PREFETCH (ops[i + 1]->src, 4 * CLIB_CACHE_PREFETCH_BYTES, LOAD);
+ CLIB_PREFETCH (ops[i + 1]->dst, 4 * CLIB_CACHE_PREFETCH_BYTES,
+ STORE);
+
+ CLIB_PREFETCH (ops[i + 2]->src, 4 * CLIB_CACHE_PREFETCH_BYTES, LOAD);
+ CLIB_PREFETCH (ops[i + 2]->dst, 4 * CLIB_CACHE_PREFETCH_BYTES,
+ STORE);
+ }
+
ctx = ptd->evp_cipher_enc_ctx[op->key_index];
EVP_EncryptInit_ex (ctx, 0, 0, NULL, op->iv);
if (op->aad_len)
diff --git a/src/plugins/dev_armada/CMakeLists.txt b/src/plugins/dev_armada/CMakeLists.txt
new file mode 100644
index 00000000000..f955a9baa91
--- /dev/null
+++ b/src/plugins/dev_armada/CMakeLists.txt
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2022 Cisco Systems, Inc.
+
+
+find_path(MUSDK_INCLUDE_DIR NAMES mv_std.h)
+find_library(MUSDK_LIB NAMES libmusdk.a)
+
+if(NOT MUSDK_INCLUDE_DIR OR NOT MUSDK_LIB)
+ message(WARNING "Marvell MUSDK not found - dev_armada plugin disabled")
+ return()
+endif()
+
+get_filename_component(MUSDK_LIB_DIR ${MUSDK_LIB} DIRECTORY)
+set(MUSDK_LINK_FLAGS "-Wl,--whole-archive,${MUSDK_LIB_DIR}/libmusdk.a,--no-whole-archive")
+
+add_vpp_plugin(dev_armada
+ SOURCES
+ plugin.c
+ pp2/init.c
+ pp2/format.c
+ pp2/port.c
+ pp2/queue.c
+ pp2/rx.c
+ pp2/tx.c
+
+ LINK_FLAGS
+ ${MUSDK_LINK_FLAGS}
+)
+include_directories(${MUSDK_INCLUDE_DIR})
+
diff --git a/src/plugins/dev_armada/README.rst b/src/plugins/dev_armada/README.rst
new file mode 100644
index 00000000000..2c757d04a06
--- /dev/null
+++ b/src/plugins/dev_armada/README.rst
@@ -0,0 +1,61 @@
+Armada device plugin
+=====================
+
+Overview
+--------
+
+This plugins provides native device support for Marvell PP2 network
+device, found in Marvel Armada family of SOCs.
+It uses Marvell Usermode SDK
+(`MUSDK <https://github.com/MarvellEmbeddedProcessors/musdk-marvell>`__).
+
+Prerequisites
+-------------
+
+Plugins depends on installed MUSDK and Marvell provided linux in Marvell SDK.
+Following kernel modules from MUSDK must be loaded for plugin to work:
+``musdk_cma.ko``
+``mv_pp_uio.ko``
+
+Musdk 18.09.3 compilation steps
+-------------------------------
+
+::
+
+ ./bootstrap
+ ./configure --prefix=/opt/vpp/external/aarch64/ CFLAGS="-Wno-error=unused-result -g -fPIC" --enable-shared=no
+ sed -i -e 's/marvell,mv-pp-uio/generic-uio/' modules/pp2/mv_pp_uio.c
+ sed -i -e 's/O_CREAT/O_CREAT, S_IRUSR | S_IWUSR/' src/lib/file_utils.c
+ make
+ sudo make install
+
+Usage
+-----
+
+Interface Creation and Deletion
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Interfaces are using new vnet dev APIs, CLIs or startup.conf to create and
+delete interfaces.
+
+Sample startup.conf:
+
+::
+
+ devices {
+ dev platform/f2000000.ethernet {
+ port 1 { name ppio1 }
+ }
+
+Device identifier in this example is 'platform/f2000000.ethernet' where
+'platform' is bus name and 'f2000000.ethernet' is linux platform bus
+identifier for specific PP2.
+
+Platform identifier can be found in sysfs:
+
+::
+
+ $ ls /sys/bus/platform/devices | grep ethernet
+ f2000000.ethernet
+
+
diff --git a/src/plugins/dev_armada/musdk.h b/src/plugins/dev_armada/musdk.h
new file mode 100644
index 00000000000..aad2f4a1cef
--- /dev/null
+++ b/src/plugins/dev_armada/musdk.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#ifndef _MUSDK_H_
+#define _MUSDK_H_
+
+#define MVCONF_DBG_LEVEL 0
+#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32
+#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64
+#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64
+#define MVCONF_SYS_DMA_UIO
+#define MVCONF_TYPES_PUBLIC
+#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC
+
+#include <mv_std.h>
+#include <env/mv_sys_dma.h>
+#include <drivers/mv_pp2.h>
+#include <drivers/mv_pp2_bpool.h>
+#include <drivers/mv_pp2_ppio.h>
+
+#endif /* _MUSDK_H_ */
diff --git a/src/plugins/dev_armada/plugin.c b/src/plugins/dev_armada/plugin.c
new file mode 100644
index 00000000000..1dc465c9a25
--- /dev/null
+++ b/src/plugins/dev_armada/plugin.c
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Marvell Armada Drivers",
+};
diff --git a/src/plugins/dev_armada/pp2/format.c b/src/plugins/dev_armada/pp2/format.c
new file mode 100644
index 00000000000..37d482b5ce8
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/format.c
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/counters.h>
+#include <vnet/dev/bus/platform.h>
+#include <dev_armada/musdk.h>
+#include <dev_armada/pp2/pp2.h>
+
+static inline u32
+mrvl_get_u32_bits (void *start, int offset, int first, int last)
+{
+ u32 value = *(u32 *) (((u8 *) start) + offset);
+ if ((last == 0) && (first == 31))
+ return value;
+ value >>= last;
+ value &= (1 << (first - last + 1)) - 1;
+ return value;
+}
+
+u8 *
+format_pp2_ppio_link_info (u8 *s, va_list *args)
+{
+ struct pp2_ppio_link_info *li = va_arg (*args, struct pp2_ppio_link_info *);
+
+ char *port_duplex[] = {
+ [MV_NET_LINK_DUPLEX_HALF] = "half",
+ [MV_NET_LINK_DUPLEX_FULL] = "full",
+ };
+
+ u32 port_speeds[] = {
+ [MV_NET_LINK_SPEED_10] = 10, [MV_NET_LINK_SPEED_100] = 100,
+ [MV_NET_LINK_SPEED_1000] = 1000, [MV_NET_LINK_SPEED_2500] = 2500,
+ [MV_NET_LINK_SPEED_10000] = 10000,
+ };
+
+ char *port_phy_modes[] = {
+ [MV_NET_PHY_MODE_NONE] = "NONE",
+ [MV_NET_PHY_MODE_MII] = "MII",
+ [MV_NET_PHY_MODE_GMII] = "GMII",
+ [MV_NET_PHY_MODE_SGMII] = "SGMII",
+ [MV_NET_PHY_MODE_TBI] = "TBI",
+ [MV_NET_PHY_MODE_REVMII] = "REVMII",
+ [MV_NET_PHY_MODE_RMII] = "RMII",
+ [MV_NET_PHY_MODE_RGMII] = "RGMII",
+ [MV_NET_PHY_MODE_RGMII_ID] = "RGMII_ID",
+ [MV_NET_PHY_MODE_RGMII_RXID] = "RGMII_RXID",
+ [MV_NET_PHY_MODE_RGMII_TXID] = "RGMII_TXID",
+ [MV_NET_PHY_MODE_RTBI] = "RTBI",
+ [MV_NET_PHY_MODE_SMII] = "SMII",
+ [MV_NET_PHY_MODE_XGMII] = "XGMII",
+ [MV_NET_PHY_MODE_MOCA] = "MOCA",
+ [MV_NET_PHY_MODE_QSGMII] = "QSGMII",
+ [MV_NET_PHY_MODE_XAUI] = "XAUI",
+ [MV_NET_PHY_MODE_RXAUI] = "RXAUI",
+ [MV_NET_PHY_MODE_KR] = "KR",
+ };
+
+ s =
+ format (s, "duplex %s speed %d up %d phy_mode %s", port_duplex[li->duplex],
+ port_speeds[li->speed], li->up, port_phy_modes[li->phy_mode]);
+
+ return s;
+}
+
+u8 *
+format_mvpp2_port_status (u8 *s, va_list *args)
+{
+ vnet_dev_format_args_t __clib_unused *a =
+ va_arg (*args, vnet_dev_format_args_t *);
+ vnet_dev_port_t *port = va_arg (*args, vnet_dev_port_t *);
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ struct pp2_ppio_link_info li = {};
+
+ if (mp->ppio == 0 || pp2_ppio_get_link_info (mp->ppio, &li))
+ return format (s, "link info not available");
+
+ return format (s, "%U", format_pp2_ppio_link_info, &li);
+}
+
+u8 *
+format_mvpp2_dev_info (u8 *s, va_list *args)
+{
+ vnet_dev_format_args_t __clib_unused *a =
+ va_arg (*args, vnet_dev_format_args_t *);
+ vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+
+ format (s, "pp_id is %u", md->pp_id);
+ return s;
+}
+
+#define foreach_pp2_rx_desc_field \
+ _ (0x00, 6, 0, l3_offset) \
+ _ (0x00, 12, 8, ip_hdlen) \
+ _ (0x00, 14, 13, ec) \
+ _ (0x00, 15, 15, es) \
+ _ (0x00, 19, 16, pool_id) \
+ _ (0x00, 21, 21, hwf_sync) \
+ _ (0x00, 22, 22, l4_chk_ok) \
+ _ (0x00, 23, 23, ip_frg) \
+ _ (0x00, 24, 24, ipv4_hdr_err) \
+ _ (0x00, 27, 25, l4_info) \
+ _ (0x00, 30, 28, l3_info) \
+ _ (0x00, 31, 31, buf_header) \
+ _ (0x04, 5, 0, lookup_id) \
+ _ (0x04, 8, 6, cpu_code) \
+ _ (0x04, 9, 9, pppoe) \
+ _ (0x04, 11, 10, l3_cast_info) \
+ _ (0x04, 13, 12, l2_cast_info) \
+ _ (0x04, 15, 14, vlan_info) \
+ _ (0x04, 31, 16, byte_count) \
+ _ (0x08, 11, 0, gem_port_id) \
+ _ (0x08, 13, 12, color) \
+ _ (0x08, 14, 14, gop_sop_u) \
+ _ (0x08, 15, 15, key_hash_enable) \
+ _ (0x08, 31, 16, l4chk) \
+ _ (0x0c, 31, 0, timestamp) \
+ _ (0x10, 31, 0, buf_phys_ptr_lo) \
+ _ (0x14, 7, 0, buf_phys_ptr_hi) \
+ _ (0x14, 31, 8, key_hash) \
+ _ (0x18, 31, 0, buf_virt_ptr_lo) \
+ _ (0x1c, 7, 0, buf_virt_ptr_hi) \
+ _ (0x1c, 14, 8, buf_qset_no) \
+ _ (0x1c, 15, 15, buf_type) \
+ _ (0x1c, 21, 16, mod_dscp) \
+ _ (0x1c, 24, 22, mod_pri) \
+ _ (0x1c, 25, 25, mdscp) \
+ _ (0x1c, 26, 26, mpri) \
+ _ (0x1c, 27, 27, mgpid) \
+ _ (0x1c, 31, 29, port_num)
+
+u8 *
+format_mvpp2_rx_desc (u8 *s, va_list *args)
+
+{
+ struct pp2_ppio_desc *d = va_arg (*args, struct pp2_ppio_desc *);
+ u32 indent = format_get_indent (s);
+ u32 r32;
+
+#define _(a, b, c, n) \
+ r32 = mrvl_get_u32_bits (d, a, b, c); \
+ if (r32 > 9) \
+ s = format (s, "%s %u (0x%x)", #n, r32, r32); \
+ else \
+ s = format (s, "%s %u", #n, r32); \
+ if (format_get_indent (s) > 72) \
+ s = format (s, "\n%U", format_white_space, indent + 2); \
+ else \
+ s = format (s, " ");
+
+ foreach_pp2_rx_desc_field;
+ return s;
+}
+
+u8 *
+format_mvpp2_rx_trace (u8 *s, va_list *args)
+{
+ vlib_main_t *vm = va_arg (*args, vlib_main_t *);
+ vlib_node_t *node = va_arg (*args, vlib_node_t *);
+ mvpp2_rx_trace_t *t = va_arg (*args, mvpp2_rx_trace_t *);
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 hw_if_index = t->rxq->port->intf.hw_if_index;
+ vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
+ u32 indent = format_get_indent (s);
+ struct pp2_ppio_desc *d = &t->desc;
+
+ s = format (s, "pp2: %v (%d) next-node %U", hi->name, hw_if_index,
+ format_vlib_next_node_name, vm, node->index, t->rxq->next_index);
+ s = format (s, "\n%U%U", format_white_space, indent + 2,
+ format_mvpp2_rx_desc, d);
+
+ return s;
+}
diff --git a/src/plugins/dev_armada/pp2/init.c b/src/plugins/dev_armada/pp2/init.c
new file mode 100644
index 00000000000..38ff32d8f53
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/init.c
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/counters.h>
+#include <vnet/dev/bus/platform.h>
+#include <vppinfra/ring.h>
+#include <dev_armada/musdk.h>
+#include <dev_armada/pp2/pp2.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+#include <linux/if.h>
+#include <sys/ioctl.h>
+
+#define MV_SYS_DMA_MEM_SZ (2 << 20)
+
+VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = {
+ .class_name = "armada",
+ .subclass_name = "init",
+};
+
+static int num_pp2_in_use = 0;
+static int dma_mem_initialized = 0;
+static int global_pp2_initialized = 0;
+
+#define _(f, n, s, d) \
+ { .name = #n, .desc = d, .severity = VL_COUNTER_SEVERITY_##s },
+
+vlib_error_desc_t mvpp2_rx_node_counters[] = { foreach_mvpp2_rx_node_counter };
+vlib_error_desc_t mvpp2_tx_node_counters[] = { foreach_mvpp2_tx_node_counter };
+#undef _
+
+vnet_dev_node_t mvpp2_rx_node = {
+ .error_counters = mvpp2_rx_node_counters,
+ .n_error_counters = ARRAY_LEN (mvpp2_rx_node_counters),
+ .format_trace = format_mvpp2_rx_trace,
+};
+
+vnet_dev_node_t mvpp2_tx_node = {
+ .error_counters = mvpp2_tx_node_counters,
+ .n_error_counters = ARRAY_LEN (mvpp2_tx_node_counters),
+};
+
+static u8 *
+mvpp2_probe (vlib_main_t *vm, vnet_dev_bus_index_t bus_index, void *dev_info)
+{
+ vnet_dev_bus_platform_device_info_t *di = dev_info;
+
+ if (clib_dt_node_is_compatible (di->node, "marvell,armada-7k-pp22"))
+ return format (0, "Marvell Armada Packet Processor v2.2");
+ return 0;
+}
+static void
+mvpp2_global_deinit (vlib_main_t *vm, vnet_dev_t *dev)
+{
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+ log_debug (dev, "");
+ if (--num_pp2_in_use == 0)
+ {
+ if (global_pp2_initialized)
+ {
+ for (u32 i = 0; i < ARRAY_LEN (md->thread); i++)
+ if (md->thread[i].bpool)
+ {
+ pp2_bpool_deinit (md->thread[i].bpool);
+ md->thread[i].bpool = 0;
+ }
+ for (u32 i = 0; i < ARRAY_LEN (md->hif); i++)
+ if (md->hif[i])
+ {
+ pp2_hif_deinit (md->hif[i]);
+ md->hif[i] = 0;
+ }
+
+ pp2_deinit ();
+ global_pp2_initialized = 0;
+ }
+ if (dma_mem_initialized)
+ {
+ mv_sys_dma_mem_destroy ();
+ log_debug (0, "mv_sys_dma_mem_destroy()");
+ dma_mem_initialized = 0;
+ }
+ }
+}
+
+static void
+mvpp2_deinit (vlib_main_t *vm, vnet_dev_t *dev)
+{
+ log_debug (dev, "");
+ mvpp2_global_deinit (vm, dev);
+}
+
+static vnet_dev_rv_t
+mvpp2_global_init (vlib_main_t *vm, vnet_dev_t *dev)
+{
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ int mrv;
+ u16 free_hifs, free_bpools;
+ u16 n_threads = vlib_get_n_threads ();
+
+ struct pp2_init_params init_params = {
+ .hif_reserved_map = 0xf,
+ .bm_pool_reserved_map = 0x7,
+ };
+
+ if (num_pp2_in_use++)
+ return rv;
+
+ mrv = mv_sys_dma_mem_init (MV_SYS_DMA_MEM_SZ);
+ if (mrv < 0)
+ {
+ log_err (0, "mv_sys_dma_mem_init failed, err %d", mrv);
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ goto done;
+ }
+
+ dma_mem_initialized = 1;
+ log_debug (0, "mv_sys_dma_mem_init(%u) ok", MV_SYS_DMA_MEM_SZ);
+
+ if ((mrv = pp2_init (&init_params)))
+ {
+ log_err (dev, "pp2_init failed, err %d", mrv);
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ goto done;
+ }
+
+ log_debug (dev, "pp2_init() ok");
+
+ free_hifs = pow2_mask (MVPP2_NUM_HIFS) ^ init_params.hif_reserved_map;
+ free_bpools =
+ pow2_mask (MVPP2_NUM_BPOOLS) ^ init_params.bm_pool_reserved_map;
+
+ if (n_threads > count_set_bits (free_hifs))
+ {
+ log_err (dev, "no enough HIFs (needed %u available %u)", n_threads,
+ count_set_bits (free_hifs));
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ goto done;
+ }
+
+ for (u32 i = 0; i < n_threads; i++)
+ {
+ char match[16];
+ u8 index;
+ struct pp2_hif_params hif_params = {
+ .match = match,
+ .out_size = 2048,
+ };
+ struct pp2_bpool_params bpool_params = {
+ .match = match,
+ .buff_len = vlib_buffer_get_default_data_size (vm),
+ };
+
+ index = get_lowest_set_bit_index (free_hifs);
+ free_hifs ^= 1 << index;
+ snprintf (match, sizeof (match), "hif-%u", index);
+
+ mrv = pp2_hif_init (&hif_params, md->hif + i);
+ if (mrv < 0)
+ {
+ log_err (dev, "pp2_hif_init failed for hif %u thread %u, err %d",
+ index, i, mrv);
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ goto done;
+ }
+ log_debug (dev, "pp2_hif_init(hif %u, thread %u) ok", index, i);
+
+ index = get_lowest_set_bit_index (free_bpools);
+ free_bpools ^= 1 << index;
+ snprintf (match, sizeof (match), "pool-%u:%u", md->pp_id, index);
+
+ mrv = pp2_bpool_init (&bpool_params, &md->thread[i].bpool);
+ if (mrv < 0)
+ {
+ log_err (dev, "pp2_bpool_init failed for bpool %u thread %u, err %d",
+ index, i, mrv);
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ goto done;
+ }
+ log_debug (dev, "pp2_bpool_init(bpool %u, thread %u) pool-%u:%u ok",
+ index, i, md->thread[i].bpool->pp2_id,
+ md->thread[i].bpool->id);
+ for (u32 j = 0; j < ARRAY_LEN (md->thread[0].bre); j++)
+ md->thread[i].bre[j].bpool = md->thread[i].bpool;
+ }
+
+done:
+ return rv;
+}
+
+static vnet_dev_rv_t
+mvpp2_init (vlib_main_t *vm, vnet_dev_t *dev)
+{
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev);
+ clib_dt_node_t *sc;
+ int pp_id = -1;
+
+ if (!clib_dt_node_is_compatible (dd->node, "marvell,armada-7k-pp22"))
+ return VNET_DEV_ERR_NOT_SUPPORTED;
+
+ sc = clib_dt_dereference_node (dd->node, "marvell,system-controller");
+
+ if (sc && vec_len (sc->path) > strlen ("/cpX/"))
+ {
+ if (strncmp ((char *) sc->path, "/cp0/", 4) == 0)
+ pp_id = 0;
+ else if (strncmp ((char *) sc->path, "/cp1/", 4) == 0)
+ pp_id = 1;
+ }
+
+ if (pp_id < 0)
+ return VNET_DEV_ERR_UNKNOWN_DEVICE;
+
+ if ((mvpp2_global_init (vm, dev)) != VNET_DEV_OK)
+ return rv;
+
+ md->pp_id = pp_id;
+
+ vec_foreach_pointer (cn, dd->node->child_nodes)
+ {
+ clib_dt_property_t *p;
+ char netdev_name[IFNAMSIZ];
+ struct ifreq s = {};
+ u8 ppio_id;
+ int fd, srv;
+
+ p = clib_dt_get_node_property_by_name (cn, "port-id");
+
+ if (!clib_dt_proprerty_is_u32 (p))
+ continue;
+
+ ppio_id = clib_dt_proprerty_get_u32 (p);
+ log_debug (dev, "found port with ppio id %u", ppio_id);
+
+ if (pp2_ppio_available (md->pp_id, ppio_id) == 0)
+ continue;
+
+ if (pp2_netdev_get_ifname (md->pp_id, ppio_id, netdev_name) < 0)
+ {
+ log_warn (dev, "failed to get ifname, skipping port %u ", ppio_id);
+ continue;
+ }
+
+ srv = -1;
+ if ((fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP)) >= 0)
+ {
+ strcpy (s.ifr_name, netdev_name);
+ srv = ioctl (fd, SIOCGIFHWADDR, &s);
+ close (fd);
+ }
+
+ if (srv < 0)
+ {
+ log_warn (dev, "unable to get hw address, skipping port %u",
+ ppio_id);
+ continue;
+ }
+
+ log_debug (dev, "adding ppio %u (netdev name %s, hwaddr %U)", ppio_id,
+ netdev_name, format_ethernet_address, s.ifr_addr.sa_data);
+
+ mvpp2_port_t mvpp2_port = {
+ .ppio_id = ppio_id,
+ };
+
+ vnet_dev_port_add_args_t port_add_args = {
+ .port = {
+ .attr = {
+ .type = VNET_DEV_PORT_TYPE_ETHERNET,
+ .max_rx_queues = PP2_PPIO_MAX_NUM_INQS,
+ .max_tx_queues = PP2_PPIO_MAX_NUM_OUTQS,
+ .max_supported_rx_frame_size = 9216,
+ },
+ .ops = {
+ .init = mvpp2_port_init,
+ .deinit = mvpp2_port_deinit,
+ .start = mvpp2_port_start,
+ .stop = mvpp2_port_stop,
+ .config_change = mvpp2_port_cfg_change,
+ .config_change_validate = mvpp2_port_cfg_change_validate,
+ .format_status = format_mvpp2_port_status,
+ },
+ .data_size = sizeof (mvpp2_port_t),
+ .initial_data = &mvpp2_port,
+ },
+ .rx_node = &mvpp2_rx_node,
+ .tx_node = &mvpp2_tx_node,
+ .rx_queue = {
+ .config = {
+ .data_size = sizeof (mvpp2_rxq_t),
+ .default_size = 512,
+ .multiplier = 32,
+ .min_size = 32,
+ .max_size = 4096,
+ .size_is_power_of_two = 1,
+ },
+ },
+ .tx_queue = {
+ .config = {
+ .data_size = sizeof (mvpp2_txq_t),
+ .default_size = 512,
+ .multiplier = 32,
+ .min_size = 32,
+ .max_size = 4096,
+ .size_is_power_of_two = 1,
+ },
+ .ops = {
+ .alloc = mvpp2_txq_alloc,
+ .free = mvpp2_txq_free,
+ },
+ },
+ };
+
+ vnet_dev_set_hw_addr_eth_mac (&port_add_args.port.attr.hw_addr,
+ (u8 *) s.ifr_addr.sa_data);
+
+ vnet_dev_port_add (vm, dev, ppio_id, &port_add_args);
+ }
+
+ if (rv != VNET_DEV_OK)
+ mvpp2_deinit (vm, dev);
+ return rv;
+}
+
+VNET_DEV_REGISTER_DRIVER (pp2) = {
+ .name = "mvpp2",
+ .bus = PLATFORM_BUS_NAME,
+ .device_data_sz = sizeof (mvpp2_device_t),
+ .ops = {
+ .init = mvpp2_init,
+ .deinit = mvpp2_deinit,
+ .probe = mvpp2_probe,
+ .format_info = format_mvpp2_dev_info,
+ },
+};
diff --git a/src/plugins/dev_armada/pp2/port.c b/src/plugins/dev_armada/pp2/port.c
new file mode 100644
index 00000000000..8e785e5e0e4
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/port.c
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/counters.h>
+#include <vnet/dev/bus/platform.h>
+#include <vppinfra/ring.h>
+#include <dev_armada/musdk.h>
+#include <dev_armada/pp2/pp2.h>
+
+VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = {
+ .class_name = "armada",
+ .subclass_name = "pp2-port",
+};
+
+vnet_dev_rv_t
+mvpp2_port_init (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ vnet_dev_t *dev = port->dev;
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ struct pp2_ppio_link_info li;
+ char match[16];
+ int mrv;
+
+ log_debug (port->dev, "");
+
+ snprintf (match, sizeof (match), "ppio-%d:%d", md->pp_id, port->port_id);
+
+ struct pp2_ppio_params ppio_params = {
+ .match = match,
+ .type = PP2_PPIO_T_NIC,
+ .eth_start_hdr = mp->is_dsa ? PP2_PPIO_HDR_ETH_DSA : PP2_PPIO_HDR_ETH,
+ .inqs_params = {
+ .num_tcs = 1,
+ .tcs_params[0] = {
+ .pkt_offset = 0,
+ .num_in_qs = 1,
+ .inqs_params = &(struct pp2_ppio_inq_params) { .size = 512 },
+ .pools[0][0] = md->thread[0].bpool,
+ },
+ },
+ };
+
+ foreach_vnet_dev_port_rx_queue (q, port)
+ {
+ struct pp2_ppio_outqs_params *oqs = &ppio_params.outqs_params;
+ oqs->outqs_params[0].weight = 1;
+ oqs->outqs_params[0].size = q->size;
+ oqs->num_outqs++;
+ }
+
+ mrv = pp2_ppio_init (&ppio_params, &mp->ppio);
+ if (mrv)
+ {
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ log_err (dev, "port %u ppio '%s' init failed, rv %d", port->port_id,
+ match, mrv);
+ goto done;
+ }
+ log_debug (dev, "port %u ppio '%s' init ok", port->port_id, match);
+
+ mrv = pp2_ppio_get_link_info (mp->ppio, &li);
+ if (mrv)
+ {
+ rv = VNET_DEV_ERR_INIT_FAILED;
+ log_err (dev, "failed to get link info for port %u, rv %d",
+ port->port_id, mrv);
+ goto done;
+ }
+
+ log_debug (dev, "port %u %U", port->port_id, format_pp2_ppio_link_info, &li);
+
+done:
+ if (rv != VNET_DEV_OK)
+ mvpp2_port_stop (vm, port);
+ return rv;
+}
+
+void
+mvpp2_port_deinit (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+
+ log_debug (port->dev, "");
+
+ if (mp->ppio)
+ {
+ pp2_ppio_deinit (mp->ppio);
+ mp->ppio = 0;
+ }
+}
+
+void
+mvpp2_port_poll (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ vnet_dev_t *dev = port->dev;
+ vnet_dev_port_state_changes_t changes = {};
+ struct pp2_ppio_link_info li;
+ int mrv;
+
+ mrv = pp2_ppio_get_link_info (mp->ppio, &li);
+
+ if (mrv)
+ {
+ log_debug (dev, "pp2_ppio_get_link_info: failed, rv %d", mrv);
+ return;
+ }
+
+ if (mp->last_link_info.up != li.up)
+ {
+ changes.change.link_state = 1;
+ changes.link_state = li.up != 0;
+ log_debug (dev, "link state changed to %u", changes.link_state);
+ }
+
+ if (mp->last_link_info.duplex != li.duplex)
+ {
+ changes.change.link_duplex = 1;
+ changes.full_duplex = li.duplex != 0;
+ log_debug (dev, "link full duplex changed to %u", changes.full_duplex);
+ }
+
+ if (mp->last_link_info.speed != li.speed)
+ {
+ u32 speeds[] = {
+ [MV_NET_LINK_SPEED_AN] = 0,
+ [MV_NET_LINK_SPEED_10] = 10000,
+ [MV_NET_LINK_SPEED_100] = 100000,
+ [MV_NET_LINK_SPEED_1000] = 1000000,
+ [MV_NET_LINK_SPEED_2500] = 2500000,
+ [MV_NET_LINK_SPEED_10000] = 10000000,
+ };
+
+ if (li.speed < ARRAY_LEN (speeds))
+ {
+ changes.change.link_speed = 1;
+ changes.link_speed = speeds[li.speed];
+ log_debug (dev, "link speed changed to %u", changes.link_speed);
+ }
+ }
+
+ if (changes.change.any == 0)
+ return;
+
+ mp->last_link_info = li;
+
+ vnet_dev_port_state_change (vm, port, changes);
+}
+
+vnet_dev_rv_t
+mvpp2_port_start (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ int mrv;
+
+ log_debug (port->dev, "");
+
+ mrv = pp2_ppio_enable (mp->ppio);
+ if (mrv)
+ {
+ log_err (port->dev, "pp2_ppio_enable() failed, rv %d", mrv);
+ return VNET_DEV_ERR_NOT_READY;
+ }
+
+ mp->is_enabled = 1;
+
+ vnet_dev_poll_port_add (vm, port, 0.5, mvpp2_port_poll);
+
+ return VNET_DEV_OK;
+}
+
+void
+mvpp2_port_stop (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ int rv;
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+
+ log_debug (port->dev, "");
+
+ if (mp->is_enabled)
+ {
+ vnet_dev_poll_port_remove (vm, port, mvpp2_port_poll);
+
+ rv = pp2_ppio_disable (mp->ppio);
+ if (rv)
+ log_err (port->dev, "pp2_ppio_disable() failed, rv %d", rv);
+
+ vnet_dev_port_state_change (vm, port,
+ (vnet_dev_port_state_changes_t){
+ .change.link_state = 1,
+ .change.link_speed = 1,
+ .link_speed = 0,
+ .link_state = 0,
+ });
+ mp->is_enabled = 0;
+ }
+}
+
+vnet_dev_rv_t
+mvpp2_port_cfg_change_validate (vlib_main_t *vm, vnet_dev_port_t *port,
+ vnet_dev_port_cfg_change_req_t *req)
+{
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+
+ switch (req->type)
+ {
+ case VNET_DEV_PORT_CFG_PROMISC_MODE:
+ case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
+ case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR:
+ break;
+
+ default:
+ rv = VNET_DEV_ERR_NOT_SUPPORTED;
+ };
+
+ return rv;
+}
+
+vnet_dev_rv_t
+mvpp2_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port,
+ vnet_dev_port_cfg_change_req_t *req)
+{
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ eth_addr_t addr;
+ int mrv;
+
+ switch (req->type)
+ {
+
+ case VNET_DEV_PORT_CFG_PROMISC_MODE:
+ mrv = pp2_ppio_set_promisc (mp->ppio, req->promisc);
+ if (mrv)
+ {
+ log_err (port->dev, "pp2_ppio_set_promisc: failed, rv %d", mrv);
+ rv = VNET_DEV_ERR_INTERNAL;
+ }
+ else
+ log_debug (port->dev, "pp2_ppio_set_promisc: promisc %u",
+ req->promisc);
+ break;
+
+ case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
+ clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr));
+ mrv = pp2_ppio_add_mac_addr (mp->ppio, addr);
+ if (mrv)
+ {
+ log_err (port->dev, "pp2_ppio_add_mac_addr: failed, rv %d", mrv);
+ rv = VNET_DEV_ERR_INTERNAL;
+ }
+ else
+ log_debug (port->dev, "pp2_ppio_add_mac_addr: %U added",
+ format_ethernet_address, &addr);
+ break;
+
+ case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR:
+ clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr));
+ mrv = pp2_ppio_remove_mac_addr (mp->ppio, addr);
+ if (mrv)
+ {
+ log_err (port->dev, "pp2_ppio_remove_mac_addr: failed, rv %d", mrv);
+ rv = VNET_DEV_ERR_INTERNAL;
+ }
+ else
+ log_debug (port->dev, "pp2_ppio_remove_mac_addr: %U added",
+ format_ethernet_address, &addr);
+ break;
+
+ default:
+ return VNET_DEV_ERR_NOT_SUPPORTED;
+ };
+
+ return rv;
+}
diff --git a/src/plugins/dev_armada/pp2/pp2.h b/src/plugins/dev_armada/pp2/pp2.h
new file mode 100644
index 00000000000..6b12dc737a7
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/pp2.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#ifndef _PP2_H_
+#define _PP2_H_
+
+#include <vppinfra/clib.h>
+#include <vppinfra/error_bootstrap.h>
+#include <vppinfra/format.h>
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+
+#define MVCONF_DBG_LEVEL 0
+#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32
+#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64
+#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64
+#define MVCONF_SYS_DMA_UIO
+#define MVCONF_TYPES_PUBLIC
+#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC
+
+#include "mv_std.h"
+#include "env/mv_sys_dma.h"
+#include "drivers/mv_pp2.h"
+#include <drivers/mv_pp2_bpool.h>
+#include <drivers/mv_pp2_ppio.h>
+
+#define MVPP2_NUM_HIFS 9
+#define MVPP2_NUM_BPOOLS 16
+#define MVPP2_MAX_THREADS 4
+#define MRVL_PP2_BUFF_BATCH_SZ 32
+
+typedef struct
+{
+ u8 pp_id;
+ struct pp2_hif *hif[MVPP2_NUM_HIFS];
+ struct
+ {
+ struct pp2_bpool *bpool;
+ struct buff_release_entry bre[MRVL_PP2_BUFF_BATCH_SZ];
+ } thread[MVPP2_NUM_BPOOLS];
+
+} mvpp2_device_t;
+
+typedef struct
+{
+ u8 is_enabled : 1;
+ u8 is_dsa : 1;
+ struct pp2_ppio *ppio;
+ u8 ppio_id;
+ struct pp2_ppio_link_info last_link_info;
+} mvpp2_port_t;
+
+typedef struct
+{
+ u16 next;
+ u16 n_enq;
+ u32 *buffers;
+} mvpp2_txq_t;
+
+typedef struct
+{
+} mvpp2_rxq_t;
+
+typedef struct
+{
+ struct pp2_ppio_desc desc;
+ vnet_dev_rx_queue_t *rxq;
+} mvpp2_rx_trace_t;
+
+/* format.c */
+format_function_t format_pp2_ppio_link_info;
+format_function_t format_mvpp2_port_status;
+format_function_t format_mvpp2_dev_info;
+format_function_t format_mvpp2_rx_trace;
+format_function_t format_mvpp2_rx_desc;
+
+/* port.c */
+vnet_dev_port_op_t mvpp2_port_init;
+vnet_dev_port_op_no_rv_t mvpp2_port_deinit;
+vnet_dev_port_op_t mvpp2_port_start;
+vnet_dev_port_op_no_rv_t mvpp2_port_stop;
+vnet_dev_rv_t mvpp2_port_cfg_change (vlib_main_t *, vnet_dev_port_t *,
+ vnet_dev_port_cfg_change_req_t *);
+vnet_dev_rv_t
+mvpp2_port_cfg_change_validate (vlib_main_t *, vnet_dev_port_t *,
+ vnet_dev_port_cfg_change_req_t *);
+
+/* queue.c */
+vnet_dev_tx_queue_op_t mvpp2_txq_alloc;
+vnet_dev_tx_queue_op_no_rv_t mvpp2_txq_free;
+
+/* inline funcs */
+
+#define log_debug(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_DEBUG, mvpp2_log.class, "%U" f, \
+ format_vnet_dev_log, (dev), \
+ clib_string_skip_prefix (__func__, "mvpp2_"), ##__VA_ARGS__)
+#define log_info(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_INFO, mvpp2_log.class, "%U" f, \
+ format_vnet_dev_log, (dev), 0, ##__VA_ARGS__)
+#define log_notice(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_NOTICE, mvpp2_log.class, "%U" f, \
+ format_vnet_dev_log, (dev), 0, ##__VA_ARGS__)
+#define log_warn(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_WARNING, mvpp2_log.class, "%U" f, \
+ format_vnet_dev_log, (dev), 0, ##__VA_ARGS__)
+#define log_err(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_ERR, mvpp2_log.class, "%U" f, format_vnet_dev_log, \
+ (dev), 0, ##__VA_ARGS__)
+
+#define foreach_mvpp2_tx_node_counter \
+ _ (NO_FREE_SLOTS, no_free_slots, ERROR, "no free tx slots") \
+ _ (PPIO_SEND, ppio_semd, ERROR, "pp2_ppio_send errors") \
+ _ (PPIO_GET_NUM_OUTQ_DONE, ppio_get_num_outq_done, ERROR, \
+ "pp2_ppio_get_num_outq_done errors")
+
+typedef enum
+{
+#define _(f, n, s, d) MVPP2_TX_NODE_CTR_##f,
+ foreach_mvpp2_tx_node_counter
+#undef _
+} mvpp2_tx_node_counter_t;
+
+#define foreach_mvpp2_rx_node_counter \
+ _ (PPIO_RECV, ppio_recv, ERROR, "pp2_ppio_recv error") \
+ _ (BPOOL_GET_NUM_BUFFS, bpool_get_num_bufs, ERROR, \
+ "pp2_bpool_get_num_buffs error") \
+ _ (BPOOL_PUT_BUFFS, bpool_put_buffs, ERROR, "pp2_bpool_put_buffs error") \
+ _ (BUFFER_ALLOC, buffer_alloc, ERROR, "buffer alloc error") \
+ _ (MAC_CE, mac_ce, ERROR, "MAC error (CRC error)") \
+ _ (MAC_OR, mac_or, ERROR, "overrun error") \
+ _ (MAC_RSVD, mac_rsvd, ERROR, "unknown MAC error") \
+ _ (MAC_RE, mac_re, ERROR, "resource error") \
+ _ (IP_HDR, ip_hdr, ERROR, "ip4 header error")
+
+typedef enum
+{
+#define _(f, n, s, d) MVPP2_RX_NODE_CTR_##f,
+ foreach_mvpp2_rx_node_counter
+#undef _
+} mvpp2_rx_node_counter_t;
+
+#endif /* _PP2_H_ */
diff --git a/src/plugins/dev_armada/pp2/queue.c b/src/plugins/dev_armada/pp2/queue.c
new file mode 100644
index 00000000000..05015414816
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/queue.c
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/counters.h>
+#include <vnet/dev/bus/platform.h>
+#include <vppinfra/ring.h>
+#include <dev_armada/musdk.h>
+#include <dev_armada/pp2/pp2.h>
+
+VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = {
+ .class_name = "armada",
+ .subclass_name = "pp2-queue",
+};
+
+vnet_dev_rv_t
+mvpp2_txq_alloc (vlib_main_t *vm, vnet_dev_tx_queue_t *txq)
+{
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq);
+ log_debug (txq->port->dev, "");
+
+ ASSERT (mtq->buffers == 0);
+ if (mtq->buffers == 0)
+ {
+ u32 sz = sizeof (u32) * txq->size;
+ mtq->buffers = clib_mem_alloc_aligned (sz, CLIB_CACHE_LINE_BYTES);
+ clib_memset (mtq->buffers, 0, sz);
+ }
+
+ return rv;
+}
+
+void
+mvpp2_txq_free (vlib_main_t *vm, vnet_dev_tx_queue_t *txq)
+{
+ mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq);
+
+ log_debug (txq->port->dev, "");
+ if (mtq->buffers)
+ {
+ clib_mem_free (mtq->buffers);
+ mtq->buffers = 0;
+ }
+}
diff --git a/src/plugins/dev_armada/pp2/rx.c b/src/plugins/dev_armada/pp2/rx.c
new file mode 100644
index 00000000000..81101ef9313
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/rx.c
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/dev/dev.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <dev_armada/pp2/pp2.h>
+
+static_always_inline void
+mvpp2_rx_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vnet_dev_rx_queue_t *rxq, vlib_buffer_t *b0, uword *n_trace,
+ struct pp2_ppio_desc *d)
+{
+ if (PREDICT_TRUE (vlib_trace_buffer (vm, node, rxq->next_index, b0,
+ /* follow_chain */ 0)))
+ {
+ mvpp2_rx_trace_t *tr;
+ vlib_set_trace_count (vm, node, --(*n_trace));
+ tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->desc = *d;
+ tr->rxq = rxq;
+ }
+}
+
+static_always_inline uword
+mrvl_pp2_rx_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, vnet_dev_rx_queue_t *rxq)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_dev_port_t *port = rxq->port;
+ vnet_dev_t *dev = port->dev;
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ vlib_buffer_template_t bt = rxq->buffer_template;
+ u32 thread_index = vm->thread_index;
+ uword n_trace = vlib_get_trace_count (vm, node);
+ u32 next_index = rxq->next_index;
+ u32 n_rx_packets = 0, n_rx_bytes = 0;
+ struct pp2_hif *hif = md->hif[thread_index];
+ struct pp2_ppio_desc descs[VLIB_FRAME_SIZE], *d;
+ struct pp2_bpool *bpool = md->thread[thread_index].bpool;
+ struct buff_release_entry *bre = md->thread[thread_index].bre;
+ u16 n_desc = VLIB_FRAME_SIZE;
+ u32 buffers[VLIB_FRAME_SIZE];
+ u32 n_bufs, *bi, i;
+ vlib_buffer_t *b0, *b1;
+
+ if (PREDICT_FALSE (
+ pp2_ppio_recv (mp->ppio, 0, rxq->queue_id, descs, &n_desc)))
+ {
+ vlib_error_count (vm, node->node_index, MVPP2_RX_NODE_CTR_PPIO_RECV, 1);
+ n_desc = 0;
+ }
+
+ n_rx_packets = n_desc;
+
+ for (i = 0; i < n_desc; i++)
+ buffers[i] = pp2_ppio_inq_desc_get_cookie (descs + i);
+
+ bt.current_data = 2;
+
+ for (d = descs, bi = buffers; n_desc >= 4; d += 2, bi += 2, n_desc -= 2)
+ {
+ /* prefetch */
+ b0 = vlib_get_buffer (vm, bi[0]);
+ b1 = vlib_get_buffer (vm, bi[1]);
+ b0->template = bt;
+ b1->template = bt;
+
+ n_rx_bytes += b0->current_length = pp2_ppio_inq_desc_get_pkt_len (d);
+ n_rx_bytes += b1->current_length = pp2_ppio_inq_desc_get_pkt_len (d + 1);
+
+ if (PREDICT_FALSE (n_trace > 0))
+ {
+ mvpp2_rx_trace (vm, node, rxq, b0, &n_trace, d);
+ if (n_trace > 0)
+ mvpp2_rx_trace (vm, node, rxq, b1, &n_trace, d + 1);
+ }
+ }
+
+ for (; n_desc; d++, bi++, n_desc--)
+ {
+ b0 = vlib_get_buffer (vm, bi[0]);
+ b0->template = bt;
+
+ n_rx_bytes += b0->current_length = pp2_ppio_inq_desc_get_pkt_len (d);
+
+ if (PREDICT_FALSE (n_trace > 0))
+ mvpp2_rx_trace (vm, node, rxq, b0, &n_trace, d);
+ }
+
+ vlib_buffer_enqueue_to_single_next (vm, node, buffers, next_index,
+ n_rx_packets);
+
+ vlib_increment_combined_counter (
+ vnm->interface_main.combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
+ thread_index, port->intf.sw_if_index, n_rx_packets, n_rx_bytes);
+
+ if (PREDICT_FALSE (pp2_bpool_get_num_buffs (bpool, &n_bufs)))
+ {
+ vlib_error_count (vm, node->node_index,
+ MVPP2_RX_NODE_CTR_BPOOL_GET_NUM_BUFFS, 1);
+ goto done;
+ }
+
+ n_bufs = rxq->size - n_bufs;
+ while (n_bufs >= MRVL_PP2_BUFF_BATCH_SZ)
+ {
+ u16 n_alloc, i;
+ struct buff_release_entry *e = bre;
+
+ n_alloc = vlib_buffer_alloc (vm, buffers, MRVL_PP2_BUFF_BATCH_SZ);
+ i = n_alloc;
+
+ if (PREDICT_FALSE (n_alloc == 0))
+ {
+ vlib_error_count (vm, node->node_index,
+ MVPP2_RX_NODE_CTR_BUFFER_ALLOC, 1);
+ goto done;
+ }
+
+ for (bi = buffers; i--; e++, bi++)
+ {
+
+ vlib_buffer_t *b = vlib_get_buffer (vm, bi[0]);
+ e->buff.addr = vlib_buffer_get_pa (vm, b) - 64;
+ e->buff.cookie = bi[0];
+ }
+
+ i = n_alloc;
+ if (PREDICT_FALSE (pp2_bpool_put_buffs (hif, bre, &i)))
+ {
+ vlib_error_count (vm, node->node_index,
+ MVPP2_RX_NODE_CTR_BPOOL_PUT_BUFFS, 1);
+ vlib_buffer_free (vm, buffers, n_alloc);
+ goto done;
+ }
+
+ if (PREDICT_FALSE (i != n_alloc))
+ vlib_buffer_free (vm, buffers + i, n_alloc - i);
+
+ n_bufs -= i;
+ }
+
+done:
+ return n_rx_packets;
+}
+
+VNET_DEV_NODE_FN (mvpp2_rx_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ u32 n_rx = 0;
+ foreach_vnet_dev_rx_queue_runtime (rxq, node)
+ n_rx += mrvl_pp2_rx_inline (vm, node, frame, rxq);
+ return n_rx;
+}
diff --git a/src/plugins/dev_armada/pp2/tx.c b/src/plugins/dev_armada/pp2/tx.c
new file mode 100644
index 00000000000..1e6675c9746
--- /dev/null
+++ b/src/plugins/dev_armada/pp2/tx.c
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/dev/dev.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <dev_armada/pp2/pp2.h>
+
+VNET_DEV_NODE_FN (mvpp2_tx_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ vnet_dev_tx_node_runtime_t *rt = vnet_dev_get_tx_node_runtime (node);
+ vnet_dev_tx_queue_t *txq = rt->tx_queue;
+ vnet_dev_port_t *port = txq->port;
+ vnet_dev_t *dev = port->dev;
+ mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq);
+ mvpp2_port_t *mp = vnet_dev_get_port_data (port);
+ mvpp2_device_t *md = vnet_dev_get_data (dev);
+ u8 qid = txq->queue_id;
+ u32 *buffers = vlib_frame_vector_args (frame);
+ u32 n_vectors = frame->n_vectors, n_left;
+ u16 n_sent;
+ struct pp2_ppio *ppio = mp->ppio;
+ struct pp2_hif *hif = md->hif[vm->thread_index];
+ struct pp2_ppio_desc descs[VLIB_FRAME_SIZE], *d = descs;
+ u16 sz = txq->size;
+ u16 mask = sz - 1;
+
+ if (mtq->n_enq)
+ {
+ u16 n_done = 0;
+ if (PREDICT_FALSE (pp2_ppio_get_num_outq_done (ppio, hif, qid, &n_done)))
+ vlib_error_count (vm, node->node_index,
+ MVPP2_TX_NODE_CTR_PPIO_GET_NUM_OUTQ_DONE, 1);
+
+ if (n_done)
+ {
+ vlib_buffer_free_from_ring (
+ vm, mtq->buffers, (mtq->next - mtq->n_enq) & mask, sz, n_done);
+ mtq->n_enq -= n_done;
+ }
+ }
+
+ n_sent = clib_min (n_vectors, sz - mtq->n_enq);
+
+ for (d = descs, n_left = n_sent; n_left; d++, buffers++, n_left--)
+ {
+ vlib_buffer_t *b0 = vlib_get_buffer (vm, buffers[0]);
+ u64 paddr = vlib_buffer_get_pa (vm, b0);
+
+ pp2_ppio_outq_desc_reset (d);
+ pp2_ppio_outq_desc_set_phys_addr (d, paddr + b0->current_data);
+ pp2_ppio_outq_desc_set_pkt_offset (d, 0);
+ pp2_ppio_outq_desc_set_pkt_len (d, b0->current_length);
+ }
+
+ buffers = vlib_frame_vector_args (frame);
+
+ if (pp2_ppio_send (ppio, hif, qid, descs, &n_sent))
+ {
+ n_sent = 0;
+ vlib_error_count (vm, node->node_index, MVPP2_TX_NODE_CTR_PPIO_SEND, 1);
+ }
+ else if (n_sent)
+ {
+ vlib_buffer_copy_indices_to_ring (mtq->buffers, buffers,
+ mtq->next & mask, sz, n_sent);
+ mtq->next += n_sent;
+ mtq->n_enq += n_sent;
+ }
+
+ /* free unsent buffers */
+ if (PREDICT_FALSE (n_sent != n_vectors))
+ {
+ vlib_buffer_free (vm, buffers + n_sent, n_vectors - n_sent);
+ vlib_error_count (vm, node->node_index, MVPP2_TX_NODE_CTR_NO_FREE_SLOTS,
+ n_vectors - n_sent);
+ }
+
+ return n_sent;
+}
diff --git a/src/plugins/dev_ena/ena.c b/src/plugins/dev_ena/ena.c
index ead090839c7..ed5c47ed505 100644
--- a/src/plugins/dev_ena/ena.c
+++ b/src/plugins/dev_ena/ena.c
@@ -4,7 +4,7 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
+#include <vnet/dev/bus/pci.h>
#include <dev_ena/ena.h>
#include <dev_ena/ena_inlines.h>
#include <vnet/ethernet/ethernet.h>
diff --git a/src/plugins/dev_ena/port.c b/src/plugins/dev_ena/port.c
index 2b26fefc5e3..95d8ff3a08c 100644
--- a/src/plugins/dev_ena/port.c
+++ b/src/plugins/dev_ena/port.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <dev_ena/ena.h>
#include <dev_ena/ena_inlines.h>
#include <vnet/ethernet/ethernet.h>
diff --git a/src/plugins/dev_iavf/adminq.c b/src/plugins/dev_iavf/adminq.c
index c12dc8aa2f6..2072c697033 100644
--- a/src/plugins/dev_iavf/adminq.c
+++ b/src/plugins/dev_iavf/adminq.c
@@ -5,7 +5,7 @@
#include <ctype.h>
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
+#include <vnet/dev/bus/pci.h>
#include <vnet/dev/counters.h>
#include <dev_iavf/iavf.h>
#include <dev_iavf/iavf_regs.h>
diff --git a/src/plugins/dev_iavf/counters.c b/src/plugins/dev_iavf/counters.c
index 6dcd01141f0..3ab463edb9a 100644
--- a/src/plugins/dev_iavf/counters.c
+++ b/src/plugins/dev_iavf/counters.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <dev_iavf/iavf.h>
#include <dev_iavf/virtchnl.h>
diff --git a/src/plugins/dev_iavf/format.c b/src/plugins/dev_iavf/format.c
index 9a3dde47ee9..b4a29e4e20a 100644
--- a/src/plugins/dev_iavf/format.c
+++ b/src/plugins/dev_iavf/format.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <dev_iavf/iavf.h>
#include <dev_iavf/virtchnl.h>
diff --git a/src/plugins/dev_iavf/iavf.c b/src/plugins/dev_iavf/iavf.c
index d1c2b9edc63..f13440f4161 100644
--- a/src/plugins/dev_iavf/iavf.c
+++ b/src/plugins/dev_iavf/iavf.c
@@ -4,7 +4,7 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
+#include <vnet/dev/bus/pci.h>
#include <vnet/dev/counters.h>
#include <vppinfra/ring.h>
#include <dev_iavf/iavf.h>
diff --git a/src/plugins/dev_iavf/port.c b/src/plugins/dev_iavf/port.c
index 90e81e960c4..f1578fccb59 100644
--- a/src/plugins/dev_iavf/port.c
+++ b/src/plugins/dev_iavf/port.c
@@ -4,7 +4,7 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
+#include <vnet/dev/bus/pci.h>
#include <vnet/dev/counters.h>
#include <dev_iavf/iavf.h>
#include <dev_iavf/iavf_regs.h>
@@ -91,7 +91,7 @@ iavf_port_init_rss (vlib_main_t *vm, vnet_dev_port_t *port)
.key_len = keylen,
};
- clib_memcpy (key->key, default_rss_key, sizeof (default_rss_key));
+ clib_memcpy (key->key, default_rss_key, keylen);
return iavf_vc_op_config_rss_key (vm, dev, key);
}
@@ -425,17 +425,20 @@ iavf_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port,
int is_primary)
{
iavf_port_t *ap = vnet_dev_get_port_data (port);
- virtchnl_ether_addr_list_t al = {
+ u8 buffer[VIRTCHNL_MSG_SZ (virtchnl_ether_addr_list_t, list, 1)];
+ virtchnl_ether_addr_list_t *al = (virtchnl_ether_addr_list_t *) buffer;
+
+ *al = (virtchnl_ether_addr_list_t){
.vsi_id = ap->vsi_id,
.num_elements = 1,
.list[0].primary = is_primary ? 1 : 0,
.list[0].extra = is_primary ? 0 : 1,
};
- clib_memcpy (al.list[0].addr, addr, sizeof (al.list[0].addr));
+ clib_memcpy (al->list[0].addr, addr, sizeof (al->list[0].addr));
- return is_add ? iavf_vc_op_add_eth_addr (vm, port->dev, &al) :
- iavf_vc_op_del_eth_addr (vm, port->dev, &al);
+ return is_add ? iavf_vc_op_add_eth_addr (vm, port->dev, al) :
+ iavf_vc_op_del_eth_addr (vm, port->dev, al);
}
static vnet_dev_rv_t
diff --git a/src/plugins/dev_iavf/queue.c b/src/plugins/dev_iavf/queue.c
index 113c0dbdfc7..51bf69a458a 100644
--- a/src/plugins/dev_iavf/queue.c
+++ b/src/plugins/dev_iavf/queue.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <vppinfra/ring.h>
#include <dev_iavf/iavf.h>
diff --git a/src/plugins/dev_iavf/virtchnl.c b/src/plugins/dev_iavf/virtchnl.c
index eca48106ce3..7e7715262c2 100644
--- a/src/plugins/dev_iavf/virtchnl.c
+++ b/src/plugins/dev_iavf/virtchnl.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <dev_iavf/iavf.h>
#include <dev_iavf/virtchnl.h>
diff --git a/src/plugins/dev_iavf/virtchnl_funcs.h b/src/plugins/dev_iavf/virtchnl_funcs.h
index e7f3901e0ee..0d4ab2835f4 100644
--- a/src/plugins/dev_iavf/virtchnl_funcs.h
+++ b/src/plugins/dev_iavf/virtchnl_funcs.h
@@ -9,6 +9,10 @@
#include <vnet/dev/dev.h>
#include <dev_iavf/iavf.h>
+/* The "+ 1" fakes a trailing element, but the driver requires that.
+ * Using this "wrong" macro is the easiest solution, as long as
+ * port.c uses buffer sized by the same macro as the functions here.
+ */
#define VIRTCHNL_MSG_SZ(s, e, n) STRUCT_OFFSET_OF (s, e[(n) + 1])
typedef struct
diff --git a/src/plugins/dev_octeon/CMakeLists.txt b/src/plugins/dev_octeon/CMakeLists.txt
index e8abf1a3389..c6271ecdfba 100644
--- a/src/plugins/dev_octeon/CMakeLists.txt
+++ b/src/plugins/dev_octeon/CMakeLists.txt
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright(c) 2022 Cisco Systems, Inc.
-if (NOT VPP_PLATFORM_NAME STREQUAL "octeon10")
+if (NOT VPP_PLATFORM_NAME STREQUAL "octeon10" AND NOT VPP_PLATFORM_NAME STREQUAL "octeon9")
return()
endif()
@@ -21,6 +21,10 @@ endif()
include_directories (${OCTEON_ROC_DIR}/)
+if (VPP_PLATFORM_NAME STREQUAL "octeon9")
+ add_compile_definitions(PLATFORM_OCTEON9)
+endif()
+
add_vpp_plugin(dev_octeon
SOURCES
init.c
@@ -31,6 +35,7 @@ add_vpp_plugin(dev_octeon
rx_node.c
tx_node.c
flow.c
+ counter.c
MULTIARCH_SOURCES
rx_node.c
diff --git a/src/plugins/dev_octeon/counter.c b/src/plugins/dev_octeon/counter.c
new file mode 100644
index 00000000000..6f57c1ee649
--- /dev/null
+++ b/src/plugins/dev_octeon/counter.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2024 Marvell.
+ * SPDX-License-Identifier: Apache-2.0
+ * https://spdx.org/licenses/Apache-2.0.html
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/counters.h>
+#include <dev_octeon/octeon.h>
+#include <dev_octeon/common.h>
+
+VLIB_REGISTER_LOG_CLASS (oct_log, static) = {
+ .class_name = "oct",
+ .subclass_name = "counters",
+};
+
+typedef enum
+{
+ OCT_PORT_CTR_RX_BYTES,
+ OCT_PORT_CTR_TX_BYTES,
+ OCT_PORT_CTR_RX_PACKETS,
+ OCT_PORT_CTR_TX_PACKETS,
+ OCT_PORT_CTR_RX_DROPS,
+ OCT_PORT_CTR_TX_DROPS,
+ OCT_PORT_CTR_RX_DROP_BYTES,
+ OCT_PORT_CTR_RX_UCAST,
+ OCT_PORT_CTR_TX_UCAST,
+ OCT_PORT_CTR_RX_MCAST,
+ OCT_PORT_CTR_TX_MCAST,
+ OCT_PORT_CTR_RX_BCAST,
+ OCT_PORT_CTR_TX_BCAST,
+ OCT_PORT_CTR_RX_FCS,
+ OCT_PORT_CTR_RX_ERR,
+ OCT_PORT_CTR_RX_DROP_MCAST,
+ OCT_PORT_CTR_RX_DROP_BCAST,
+ OCT_PORT_CTR_RX_DROP_L3_MCAST,
+ OCT_PORT_CTR_RX_DROP_L3_BCAST,
+} oct_port_counter_id_t;
+
+vnet_dev_counter_t oct_port_counters[] = {
+ VNET_DEV_CTR_RX_BYTES (OCT_PORT_CTR_RX_BYTES),
+ VNET_DEV_CTR_RX_PACKETS (OCT_PORT_CTR_RX_PACKETS),
+ VNET_DEV_CTR_RX_DROPS (OCT_PORT_CTR_RX_DROPS),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_BYTES, RX, BYTES, "drop bytes"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_UCAST, RX, PACKETS, "unicast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_MCAST, RX, PACKETS, "multicast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_BCAST, RX, PACKETS, "broadcast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_FCS, RX, PACKETS, "fcs"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_ERR, RX, PACKETS, "error"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_MCAST, RX, PACKETS,
+ "drop multicast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_BCAST, RX, PACKETS,
+ "drop broadcast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_L3_MCAST, RX, PACKETS,
+ "drop L3 multicast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_L3_BCAST, RX, PACKETS,
+ "drop L3 broadcast"),
+
+ VNET_DEV_CTR_TX_BYTES (OCT_PORT_CTR_TX_BYTES),
+ VNET_DEV_CTR_TX_PACKETS (OCT_PORT_CTR_TX_PACKETS),
+ VNET_DEV_CTR_TX_DROPS (OCT_PORT_CTR_TX_DROPS),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_TX_UCAST, TX, PACKETS, "unicast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_TX_MCAST, TX, PACKETS, "multicast"),
+ VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_TX_BCAST, TX, PACKETS, "broadcast"),
+};
+
+typedef enum
+{
+ OCT_RXQ_CTR_BYTES,
+ OCT_RXQ_CTR_PKTS,
+ OCT_RXQ_CTR_DROPS,
+ OCT_RXQ_CTR_DROP_BYTES,
+ OCT_RXQ_CTR_ERR,
+} oct_rxq_counter_id_t;
+
+vnet_dev_counter_t oct_rxq_counters[] = {
+ VNET_DEV_CTR_RX_BYTES (OCT_RXQ_CTR_BYTES),
+ VNET_DEV_CTR_RX_PACKETS (OCT_RXQ_CTR_PKTS),
+ VNET_DEV_CTR_RX_DROPS (OCT_RXQ_CTR_DROPS),
+ VNET_DEV_CTR_VENDOR (OCT_RXQ_CTR_DROP_BYTES, RX, BYTES, "drop bytes"),
+ VNET_DEV_CTR_VENDOR (OCT_RXQ_CTR_ERR, RX, PACKETS, "error"),
+};
+
+typedef enum
+{
+ OCT_TXQ_CTR_BYTES,
+ OCT_TXQ_CTR_PKTS,
+ OCT_TXQ_CTR_DROPS,
+ OCT_TXQ_CTR_DROP_BYTES,
+} oct_txq_counter_id_t;
+
+vnet_dev_counter_t oct_txq_counters[] = {
+ VNET_DEV_CTR_TX_BYTES (OCT_TXQ_CTR_BYTES),
+ VNET_DEV_CTR_TX_PACKETS (OCT_TXQ_CTR_PKTS),
+ VNET_DEV_CTR_TX_DROPS (OCT_TXQ_CTR_DROPS),
+ VNET_DEV_CTR_VENDOR (OCT_TXQ_CTR_DROP_BYTES, TX, BYTES, "drop bytes"),
+};
+
+static vnet_dev_rv_t
+oct_roc_err (vnet_dev_t *dev, int rv, char *fmt, ...)
+{
+ u8 *s = 0;
+ va_list va;
+
+ va_start (va, fmt);
+ s = va_format (s, fmt, &va);
+ va_end (va);
+
+ log_err (dev, "%v - ROC error %s (%d)", s, roc_error_msg_get (rv), rv);
+
+ vec_free (s);
+ return VNET_DEV_ERR_INTERNAL;
+}
+
+void
+oct_port_add_counters (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ vnet_dev_port_add_counters (vm, port, oct_port_counters,
+ ARRAY_LEN (oct_port_counters));
+
+ foreach_vnet_dev_port_rx_queue (rxq, port)
+ {
+ vnet_dev_rx_queue_add_counters (vm, rxq, oct_rxq_counters,
+ ARRAY_LEN (oct_rxq_counters));
+ }
+
+ foreach_vnet_dev_port_tx_queue (txq, port)
+ {
+ vnet_dev_tx_queue_add_counters (vm, txq, oct_txq_counters,
+ ARRAY_LEN (oct_txq_counters));
+ }
+}
+
+vnet_dev_rv_t
+oct_port_get_stats (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ vnet_dev_t *dev = port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ int rrv;
+ struct roc_nix_stats stats;
+
+ if ((rrv = roc_nix_stats_get (nix, &stats)))
+ return oct_roc_err (dev, rrv, "roc_nix_stats_get() failed");
+
+ foreach_vnet_dev_counter (c, port->counter_main)
+ {
+ switch (c->user_data)
+ {
+ case OCT_PORT_CTR_RX_BYTES:
+ vnet_dev_counter_value_update (vm, c, stats.rx_octs);
+ break;
+ case OCT_PORT_CTR_TX_BYTES:
+ vnet_dev_counter_value_update (vm, c, stats.tx_octs);
+ break;
+ case OCT_PORT_CTR_RX_PACKETS:
+ vnet_dev_counter_value_update (
+ vm, c, stats.rx_ucast + stats.rx_bcast + stats.rx_mcast);
+ break;
+ case OCT_PORT_CTR_TX_PACKETS:
+ vnet_dev_counter_value_update (
+ vm, c, stats.tx_ucast + stats.tx_bcast + stats.tx_mcast);
+ break;
+ case OCT_PORT_CTR_RX_DROPS:
+ vnet_dev_counter_value_update (vm, c, stats.rx_drop);
+ break;
+ case OCT_PORT_CTR_TX_DROPS:
+ vnet_dev_counter_value_update (vm, c, stats.tx_drop);
+ break;
+ case OCT_PORT_CTR_RX_DROP_BYTES:
+ vnet_dev_counter_value_update (vm, c, stats.rx_drop_octs);
+ break;
+ case OCT_PORT_CTR_RX_UCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_ucast);
+ break;
+ case OCT_PORT_CTR_TX_UCAST:
+ vnet_dev_counter_value_update (vm, c, stats.tx_ucast);
+ break;
+ case OCT_PORT_CTR_RX_MCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_mcast);
+ break;
+ case OCT_PORT_CTR_TX_MCAST:
+ vnet_dev_counter_value_update (vm, c, stats.tx_mcast);
+ break;
+ case OCT_PORT_CTR_RX_BCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_bcast);
+ break;
+ case OCT_PORT_CTR_TX_BCAST:
+ vnet_dev_counter_value_update (vm, c, stats.tx_bcast);
+ break;
+ case OCT_PORT_CTR_RX_FCS:
+ vnet_dev_counter_value_update (vm, c, stats.rx_fcs);
+ break;
+ case OCT_PORT_CTR_RX_ERR:
+ vnet_dev_counter_value_update (vm, c, stats.rx_err);
+ break;
+ case OCT_PORT_CTR_RX_DROP_MCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_drop_mcast);
+ break;
+ case OCT_PORT_CTR_RX_DROP_BCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_drop_bcast);
+ break;
+ case OCT_PORT_CTR_RX_DROP_L3_MCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_drop_l3_mcast);
+ break;
+ case OCT_PORT_CTR_RX_DROP_L3_BCAST:
+ vnet_dev_counter_value_update (vm, c, stats.rx_drop_l3_bcast);
+ break;
+ default:
+ ASSERT (0);
+ }
+ }
+
+ return VNET_DEV_OK;
+}
+
+vnet_dev_rv_t
+oct_rxq_get_stats (vlib_main_t *vm, vnet_dev_port_t *port,
+ vnet_dev_rx_queue_t *rxq)
+{
+ oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq);
+ struct roc_nix_stats_queue qstats;
+ vnet_dev_t *dev = port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ int rrv;
+
+ if ((rrv = roc_nix_stats_queue_get (nix, crq->rq.qid, 1, &qstats)))
+ return oct_roc_err (dev, rrv, "roc_nix_stats_queue_get() failed");
+
+ foreach_vnet_dev_counter (c, rxq->counter_main)
+ {
+ switch (c->user_data)
+ {
+ case OCT_RXQ_CTR_BYTES:
+ vnet_dev_counter_value_update (vm, c, qstats.rx_octs);
+ break;
+ case OCT_RXQ_CTR_PKTS:
+ vnet_dev_counter_value_update (vm, c, qstats.rx_pkts);
+ break;
+ case OCT_RXQ_CTR_DROPS:
+ vnet_dev_counter_value_update (vm, c, qstats.rx_drop_pkts);
+ break;
+ case OCT_RXQ_CTR_DROP_BYTES:
+ vnet_dev_counter_value_update (vm, c, qstats.rx_drop_octs);
+ break;
+ case OCT_RXQ_CTR_ERR:
+ vnet_dev_counter_value_update (vm, c, qstats.rx_error_pkts);
+ break;
+ default:
+ ASSERT (0);
+ }
+ }
+
+ return VNET_DEV_OK;
+}
+
+vnet_dev_rv_t
+oct_txq_get_stats (vlib_main_t *vm, vnet_dev_port_t *port,
+ vnet_dev_tx_queue_t *txq)
+{
+ oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
+ struct roc_nix_stats_queue qstats;
+ vnet_dev_t *dev = port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ int rrv;
+
+ if ((rrv = roc_nix_stats_queue_get (nix, ctq->sq.qid, 0, &qstats)))
+ return oct_roc_err (dev, rrv, "roc_nix_stats_queue_get() failed");
+
+ foreach_vnet_dev_counter (c, txq->counter_main)
+ {
+ switch (c->user_data)
+ {
+ case OCT_TXQ_CTR_BYTES:
+ vnet_dev_counter_value_update (vm, c, qstats.tx_octs);
+ break;
+ case OCT_TXQ_CTR_PKTS:
+ vnet_dev_counter_value_update (vm, c, qstats.tx_pkts);
+ break;
+ case OCT_TXQ_CTR_DROPS:
+ vnet_dev_counter_value_update (vm, c, qstats.tx_drop_pkts);
+ break;
+ case OCT_TXQ_CTR_DROP_BYTES:
+ vnet_dev_counter_value_update (vm, c, qstats.tx_drop_octs);
+ break;
+ default:
+ ASSERT (0);
+ }
+ }
+
+ return VNET_DEV_OK;
+}
+
+void
+oct_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port)
+{
+ vnet_dev_t *dev = port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ int rrv;
+
+ if ((rrv = roc_nix_stats_reset (nix)))
+ oct_roc_err (dev, rrv, "roc_nix_stats_reset() failed");
+}
+
+void
+oct_rxq_clear_counters (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq)
+{
+ oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq);
+ vnet_dev_t *dev = rxq->port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ int rrv;
+
+ if ((rrv = roc_nix_stats_queue_reset (nix, crq->rq.qid, 1)))
+ oct_roc_err (dev, rrv,
+ "roc_nix_stats_queue_reset() failed for rx queue %u",
+ rxq->queue_id);
+}
+
+void
+oct_txq_clear_counters (vlib_main_t *vm, vnet_dev_tx_queue_t *txq)
+{
+ oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
+ vnet_dev_t *dev = txq->port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ int rrv;
+
+ if ((rrv = roc_nix_stats_queue_reset (nix, ctq->sq.qid, 0)))
+ oct_roc_err (dev, rrv,
+ "roc_nix_stats_queue_reset() failed for tx queue %u",
+ txq->queue_id);
+}
diff --git a/src/plugins/dev_octeon/flow.c b/src/plugins/dev_octeon/flow.c
index 1c367a036ab..5bef25f5369 100644
--- a/src/plugins/dev_octeon/flow.c
+++ b/src/plugins/dev_octeon/flow.c
@@ -46,6 +46,8 @@ VLIB_REGISTER_LOG_CLASS (oct_log, static) = {
(f->type == VNET_FLOW_TYPE_IP4_GTPC) || \
(f->type == VNET_FLOW_TYPE_IP4_GTPU))
+#define FLOW_IS_GENERIC_TYPE(f) (f->type == VNET_FLOW_TYPE_GENERIC)
+
#define OCT_FLOW_UNSUPPORTED_ACTIONS(f) \
((f->actions == VNET_FLOW_ACTION_BUFFER_ADVANCE) || \
(f->actions == VNET_FLOW_ACTION_REDIRECT_TO_NODE))
@@ -71,6 +73,9 @@ VLIB_REGISTER_LOG_CLASS (oct_log, static) = {
_ (62, FLOW_KEY_TYPE_L3_DST, "l3-dst-only") \
_ (63, FLOW_KEY_TYPE_L3_SRC, "l3-src-only")
+#define GTPU_PORT 2152
+#define VXLAN_PORT 4789
+
typedef struct
{
u16 src_port;
@@ -87,6 +92,27 @@ typedef struct
u32 teid;
} gtpu_header_t;
+typedef struct
+{
+ u8 layer;
+ u16 nxt_proto;
+ vnet_dev_port_t *port;
+ struct roc_npc_item_info *items;
+ struct
+ {
+ u8 *spec;
+ u8 *mask;
+ u16 off;
+ } oct_drv;
+ struct
+ {
+ u8 *spec;
+ u8 *mask;
+ u16 off;
+ u16 len;
+ } generic;
+} oct_flow_parse_state;
+
static void
oct_flow_convert_rss_types (u64 *key, u64 rss_types)
{
@@ -163,6 +189,14 @@ oct_flow_rule_create (vnet_dev_port_t *port, struct roc_npc_action *actions,
npc = &oct_port->npc;
+ for (int i = 0; item_info[i].type != ROC_NPC_ITEM_TYPE_END; i++)
+ {
+ log_debug (port->dev, "Flow[%d] Item[%d] type %d spec 0x%U mask 0x%U",
+ flow->index, i, item_info[i].type, format_hex_bytes,
+ item_info[i].spec, item_info[i].size, format_hex_bytes,
+ item_info[i].mask, item_info[i].size);
+ }
+
npc_flow =
roc_npc_flow_create (npc, &attr, item_info, actions, npc->pf_func, &rv);
if (rv)
@@ -183,6 +217,320 @@ oct_flow_rule_create (vnet_dev_port_t *port, struct roc_npc_action *actions,
return VNET_DEV_OK;
}
+static int
+oct_parse_l2 (oct_flow_parse_state *pst)
+{
+ struct roc_npc_flow_item_eth *eth_spec =
+ (struct roc_npc_flow_item_eth *) &pst->oct_drv.spec[pst->oct_drv.off];
+ struct roc_npc_flow_item_eth *eth_mask =
+ (struct roc_npc_flow_item_eth *) &pst->oct_drv.mask[pst->oct_drv.off];
+ ethernet_header_t *eth_hdr_mask =
+ (ethernet_header_t *) &pst->generic.mask[pst->generic.off];
+ ethernet_header_t *eth_hdr =
+ (ethernet_header_t *) &pst->generic.spec[pst->generic.off];
+ u16 tpid, etype;
+
+ tpid = etype = clib_net_to_host_u16 (eth_hdr->type);
+ clib_memcpy_fast (eth_spec, eth_hdr, sizeof (ethernet_header_t));
+ clib_memcpy_fast (eth_mask, eth_hdr_mask, sizeof (ethernet_header_t));
+ eth_spec->has_vlan = 0;
+
+ pst->items[pst->layer].spec = (void *) eth_spec;
+ pst->items[pst->layer].mask = (void *) eth_mask;
+ pst->items[pst->layer].size = sizeof (ethernet_header_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_ETH;
+ pst->generic.off += sizeof (ethernet_header_t);
+ pst->oct_drv.off += sizeof (struct roc_npc_flow_item_eth);
+ pst->layer++;
+
+ /* Parse VLAN Tags if any */
+ struct roc_npc_flow_item_vlan *vlan_spec =
+ (struct roc_npc_flow_item_vlan *) &pst->oct_drv.spec[pst->oct_drv.off];
+ struct roc_npc_flow_item_vlan *vlan_mask =
+ (struct roc_npc_flow_item_vlan *) &pst->oct_drv.mask[pst->oct_drv.off];
+ ethernet_vlan_header_t *vlan_hdr, *vlan_hdr_mask;
+ u8 vlan_cnt = 0;
+
+ while (tpid == ETHERNET_TYPE_DOT1AD || tpid == ETHERNET_TYPE_VLAN)
+ {
+ if (pst->generic.off >= pst->generic.len)
+ break;
+
+ vlan_hdr =
+ (ethernet_vlan_header_t *) &pst->generic.spec[pst->generic.off];
+ vlan_hdr_mask =
+ (ethernet_vlan_header_t *) &pst->generic.mask[pst->generic.off];
+ tpid = etype = clib_net_to_host_u16 (vlan_hdr->type);
+ clib_memcpy (&vlan_spec[vlan_cnt], vlan_hdr,
+ sizeof (ethernet_vlan_header_t));
+ clib_memcpy (&vlan_mask[vlan_cnt], vlan_hdr_mask,
+ sizeof (ethernet_vlan_header_t));
+ pst->items[pst->layer].spec = (void *) &vlan_spec[vlan_cnt];
+ pst->items[pst->layer].mask = (void *) &vlan_mask[vlan_cnt];
+ pst->items[pst->layer].size = sizeof (ethernet_vlan_header_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_VLAN;
+ pst->generic.off += sizeof (ethernet_vlan_header_t);
+ pst->oct_drv.off += sizeof (struct roc_npc_flow_item_vlan);
+ pst->layer++;
+ vlan_cnt++;
+ }
+
+ /* Inner most vlan tag */
+ if (vlan_cnt)
+ vlan_spec[vlan_cnt - 1].has_more_vlan = 0;
+
+ pst->nxt_proto = etype;
+ return 0;
+}
+
+static int
+oct_parse_l3 (oct_flow_parse_state *pst)
+{
+
+ if (pst->generic.off >= pst->generic.len || pst->nxt_proto == 0)
+ return 0;
+
+ if (pst->nxt_proto == ETHERNET_TYPE_MPLS)
+ {
+ int label_stack_bottom = 0;
+ do
+ {
+
+ u8 *mpls_spec = &pst->generic.spec[pst->generic.off];
+ u8 *mpls_mask = &pst->generic.mask[pst->generic.off];
+
+ label_stack_bottom = mpls_spec[2] & 1;
+ pst->items[pst->layer].spec = (void *) mpls_spec;
+ pst->items[pst->layer].mask = (void *) mpls_mask;
+ pst->items[pst->layer].size = sizeof (u32);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_MPLS;
+ pst->generic.off += sizeof (u32);
+ pst->layer++;
+ }
+ while (label_stack_bottom);
+
+ pst->nxt_proto = 0;
+ return 0;
+ }
+ else if (pst->nxt_proto == ETHERNET_TYPE_IP4)
+ {
+ ip4_header_t *ip4_spec =
+ (ip4_header_t *) &pst->generic.spec[pst->generic.off];
+ ip4_header_t *ip4_mask =
+ (ip4_header_t *) &pst->generic.mask[pst->generic.off];
+ pst->items[pst->layer].spec = (void *) ip4_spec;
+ pst->items[pst->layer].mask = (void *) ip4_mask;
+ pst->items[pst->layer].size = sizeof (ip4_header_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV4;
+ pst->generic.off += sizeof (ip4_header_t);
+ pst->layer++;
+ pst->nxt_proto = ip4_spec->protocol;
+ }
+ else if (pst->nxt_proto == ETHERNET_TYPE_IP6)
+ {
+ struct roc_npc_flow_item_ipv6 *ip6_spec =
+ (struct roc_npc_flow_item_ipv6 *) &pst->oct_drv.spec[pst->oct_drv.off];
+ struct roc_npc_flow_item_ipv6 *ip6_mask =
+ (struct roc_npc_flow_item_ipv6 *) &pst->oct_drv.mask[pst->oct_drv.off];
+ ip6_header_t *ip6_hdr_mask =
+ (ip6_header_t *) &pst->generic.mask[pst->generic.off];
+ ip6_header_t *ip6_hdr =
+ (ip6_header_t *) &pst->generic.spec[pst->generic.off];
+ u8 nxt_hdr = ip6_hdr->protocol;
+
+ clib_memcpy (ip6_spec, ip6_hdr, sizeof (ip6_header_t));
+ clib_memcpy (ip6_mask, ip6_hdr_mask, sizeof (ip6_header_t));
+ pst->items[pst->layer].spec = (void *) ip6_spec;
+ pst->items[pst->layer].mask = (void *) ip6_mask;
+ pst->items[pst->layer].size = sizeof (ip6_header_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV6;
+ pst->generic.off += sizeof (ip6_header_t);
+ pst->oct_drv.off += sizeof (struct roc_npc_flow_item_ipv6);
+ pst->layer++;
+
+ while (nxt_hdr == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS ||
+ nxt_hdr == IP_PROTOCOL_IP6_DESTINATION_OPTIONS ||
+ nxt_hdr == IP_PROTOCOL_IPV6_ROUTE)
+ {
+ if (pst->generic.off >= pst->generic.len)
+ return 0;
+
+ ip6_ext_header_t *ip6_ext_spec =
+ (ip6_ext_header_t *) &pst->generic.spec[pst->generic.off];
+ ip6_ext_header_t *ip6_ext_mask =
+ (ip6_ext_header_t *) &pst->generic.mask[pst->generic.off];
+ nxt_hdr = ip6_ext_spec->next_hdr;
+
+ pst->items[pst->layer].spec = (void *) ip6_ext_spec;
+ pst->items[pst->layer].mask = (void *) ip6_ext_mask;
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV6_EXT;
+ pst->generic.off += ip6_ext_header_len (ip6_ext_spec);
+ pst->layer++;
+ }
+
+ if (pst->generic.off >= pst->generic.len)
+ return 0;
+
+ if (nxt_hdr == IP_PROTOCOL_IPV6_FRAGMENTATION)
+ {
+ ip6_frag_hdr_t *ip6_ext_frag_spec =
+ (ip6_frag_hdr_t *) &pst->generic.spec[pst->generic.off];
+ ip6_frag_hdr_t *ip6_ext_frag_mask =
+ (ip6_frag_hdr_t *) &pst->generic.mask[pst->generic.off];
+
+ pst->items[pst->layer].spec = (void *) ip6_ext_frag_spec;
+ pst->items[pst->layer].mask = (void *) ip6_ext_frag_mask;
+ pst->items[pst->layer].size = sizeof (ip6_frag_hdr_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV6_FRAG_EXT;
+ pst->generic.off += sizeof (ip6_frag_hdr_t);
+ pst->layer++;
+ }
+
+ pst->nxt_proto = nxt_hdr;
+ }
+ /* Unsupported L3. */
+ else
+ return -1;
+
+ return 0;
+}
+
+static int
+oct_parse_l4 (oct_flow_parse_state *pst)
+{
+
+ if (pst->generic.off >= pst->generic.len || pst->nxt_proto == 0)
+ return 0;
+
+#define _(protocol_t, protocol_value, ltype) \
+ if (pst->nxt_proto == protocol_value) \
+ \
+ { \
+ \
+ protocol_t *spec = (protocol_t *) &pst->generic.spec[pst->generic.off]; \
+ protocol_t *mask = (protocol_t *) &pst->generic.mask[pst->generic.off]; \
+ pst->items[pst->layer].spec = spec; \
+ pst->items[pst->layer].mask = mask; \
+ \
+ pst->items[pst->layer].size = sizeof (protocol_t); \
+ \
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_##ltype; \
+ pst->generic.off += sizeof (protocol_t); \
+ pst->layer++; \
+ return 0; \
+ }
+
+ _ (esp_header_t, IP_PROTOCOL_IPSEC_ESP, ESP)
+ _ (udp_header_t, IP_PROTOCOL_UDP, UDP)
+ _ (tcp_header_t, IP_PROTOCOL_TCP, TCP)
+ _ (sctp_header_t, IP_PROTOCOL_SCTP, SCTP)
+ _ (icmp46_header_t, IP_PROTOCOL_ICMP, ICMP)
+ _ (icmp46_header_t, IP_PROTOCOL_ICMP6, ICMP)
+ _ (igmp_header_t, IP_PROTOCOL_IGMP, IGMP)
+ _ (gre_header_t, IP_PROTOCOL_GRE, GRE)
+
+ /* Unsupported L4. */
+ return -1;
+}
+
+static int
+oct_parse_tunnel (oct_flow_parse_state *pst)
+{
+ if (pst->generic.off >= pst->generic.len)
+ return 0;
+
+ if (pst->items[pst->layer - 1].type == ROC_NPC_ITEM_TYPE_GRE)
+ {
+ gre_header_t *gre_hdr = (gre_header_t *) pst->items[pst->layer - 1].spec;
+ pst->nxt_proto = clib_net_to_host_u16 (gre_hdr->protocol);
+ goto parse_l3;
+ }
+
+ else if (pst->items[pst->layer - 1].type == ROC_NPC_ITEM_TYPE_UDP)
+ {
+ udp_header_t *udp_h = (udp_header_t *) pst->items[pst->layer - 1].spec;
+ u16 dport = clib_net_to_host_u16 (udp_h->dst_port);
+
+ if (dport == GTPU_PORT)
+ {
+ gtpu_header_t *gtpu_spec =
+ (gtpu_header_t *) &pst->generic.spec[pst->generic.off];
+ gtpu_header_t *gtpu_mask =
+ (gtpu_header_t *) &pst->generic.mask[pst->generic.off];
+ pst->items[pst->layer].spec = (void *) gtpu_spec;
+ pst->items[pst->layer].mask = (void *) gtpu_mask;
+ pst->items[pst->layer].size = sizeof (gtpu_header_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_GTPU;
+ pst->generic.off += sizeof (gtpu_header_t);
+ pst->layer++;
+ pst->nxt_proto = 0;
+ return 0;
+ }
+ else if (dport == VXLAN_PORT)
+ {
+ vxlan_header_t *vxlan_spec =
+ (vxlan_header_t *) &pst->generic.spec[pst->generic.off];
+ vxlan_header_t *vxlan_mask =
+ (vxlan_header_t *) &pst->generic.spec[pst->generic.off];
+ pst->items[pst->layer].spec = (void *) vxlan_spec;
+ pst->items[pst->layer].mask = (void *) vxlan_mask;
+ pst->items[pst->layer].size = sizeof (vxlan_header_t);
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_VXLAN;
+ pst->generic.off += sizeof (vxlan_header_t);
+ pst->layer++;
+ pst->nxt_proto = 0;
+ goto parse_l2;
+ }
+ }
+ /* No supported Tunnel detected. */
+ else
+ {
+ log_err (pst->port->dev,
+ "Partially parsed till offset %u, not able to parse further",
+ pst->generic.off);
+ return 0;
+ }
+parse_l2:
+ if (oct_parse_l2 (pst))
+ return -1;
+parse_l3:
+ if (oct_parse_l3 (pst))
+ return -1;
+
+ return oct_parse_l4 (pst);
+}
+
+static vnet_dev_rv_t
+oct_flow_generic_pattern_parse (oct_flow_parse_state *pst)
+{
+
+ if (oct_parse_l2 (pst))
+ goto err;
+
+ if (oct_parse_l3 (pst))
+ goto err;
+
+ if (oct_parse_l4 (pst))
+ goto err;
+
+ if (oct_parse_tunnel (pst))
+ goto err;
+
+ if (pst->generic.off < pst->generic.len)
+ {
+ log_err (pst->port->dev,
+ "Partially parsed till offset %u, not able to parse further",
+ pst->generic.off);
+ goto err;
+ }
+
+ pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_END;
+ return VNET_DEV_OK;
+
+err:
+ return VNET_DEV_ERR_NOT_SUPPORTED;
+}
+
static vnet_dev_rv_t
oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
uword *private_data)
@@ -190,12 +538,22 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
struct roc_npc_item_info item_info[ROC_NPC_ITEM_TYPE_END] = {};
struct roc_npc_action actions[ROC_NPC_ITEM_TYPE_END] = {};
oct_port_t *oct_port = vnet_dev_get_port_data (port);
+ ethernet_header_t eth_spec = {}, eth_mask = {};
+ sctp_header_t sctp_spec = {}, sctp_mask = {};
+ gtpu_header_t gtpu_spec = {}, gtpu_mask = {};
+ ip4_header_t ip4_spec = {}, ip4_mask = {};
+ ip6_header_t ip6_spec = {}, ip6_mask = {};
+ udp_header_t udp_spec = {}, udp_mask = {};
+ tcp_header_t tcp_spec = {}, tcp_mask = {};
+ esp_header_t esp_spec = {}, esp_mask = {};
u16 l4_src_port = 0, l4_dst_port = 0;
u16 l4_src_mask = 0, l4_dst_mask = 0;
struct roc_npc_action_rss rss_conf = {};
struct roc_npc_action_queue conf = {};
struct roc_npc_action_mark mark = {};
struct roc_npc *npc = &oct_port->npc;
+ u8 *flow_spec = 0, *flow_mask = 0;
+ u8 *drv_spec = 0, *drv_mask = 0;
vnet_dev_rv_t rv = VNET_DEV_OK;
int layer = 0, index = 0;
u16 *queues = NULL;
@@ -203,11 +561,52 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
u8 proto = 0;
u16 action = 0;
+ if (FLOW_IS_GENERIC_TYPE (flow))
+ {
+ unformat_input_t input;
+ int rc;
+
+ unformat_init_string (
+ &input, (const char *) flow->generic.pattern.spec,
+ strlen ((const char *) flow->generic.pattern.spec));
+ unformat_user (&input, unformat_hex_string, &flow_spec);
+ unformat_free (&input);
+
+ unformat_init_string (
+ &input, (const char *) flow->generic.pattern.mask,
+ strlen ((const char *) flow->generic.pattern.mask));
+ unformat_user (&input, unformat_hex_string, &flow_mask);
+ unformat_free (&input);
+
+ vec_validate (drv_spec, 1024);
+ vec_validate (drv_mask, 1024);
+ oct_flow_parse_state pst = {
+ .nxt_proto = 0,
+ .port = port,
+ .items = item_info,
+ .oct_drv = { .spec = drv_spec, .mask = drv_mask },
+ .generic = { .spec = flow_spec,
+ .mask = flow_mask,
+ .len = vec_len (flow_spec) },
+ };
+
+ rc = oct_flow_generic_pattern_parse (&pst);
+ if (rc)
+ {
+ vec_free (flow_spec);
+ vec_free (flow_mask);
+ vec_free (drv_spec);
+ vec_free (drv_mask);
+ return VNET_DEV_ERR_NOT_SUPPORTED;
+ }
+
+ goto parse_flow_actions;
+ }
+
if (FLOW_IS_ETHERNET_CLASS (flow))
{
- ethernet_header_t eth_spec = { .type = clib_host_to_net_u16 (
- flow->ethernet.eth_hdr.type) },
- eth_mask = { .type = 0xFFFF };
+ eth_spec.type = clib_host_to_net_u16 (flow->ethernet.eth_hdr.type);
+ eth_mask.type = 0xFFFF;
item_info[layer].spec = (void *) &eth_spec;
item_info[layer].mask = (void *) &eth_mask;
@@ -220,10 +619,11 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
{
vnet_flow_ip4_t *ip4_hdr = &flow->ip4;
proto = ip4_hdr->protocol.prot;
- ip4_header_t ip4_spec = { .src_address = ip4_hdr->src_addr.addr,
- .dst_address = ip4_hdr->dst_addr.addr },
- ip4_mask = { .src_address = ip4_hdr->src_addr.mask,
- .dst_address = ip4_hdr->dst_addr.mask };
+
+ ip4_spec.src_address = ip4_hdr->src_addr.addr;
+ ip4_spec.dst_address = ip4_hdr->dst_addr.addr;
+ ip4_mask.src_address = ip4_hdr->src_addr.mask;
+ ip4_mask.dst_address = ip4_hdr->dst_addr.mask;
item_info[layer].spec = (void *) &ip4_spec;
item_info[layer].mask = (void *) &ip4_mask;
@@ -245,10 +645,11 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
{
vnet_flow_ip6_t *ip6_hdr = &flow->ip6;
proto = ip6_hdr->protocol.prot;
- ip6_header_t ip6_spec = { .src_address = ip6_hdr->src_addr.addr,
- .dst_address = ip6_hdr->dst_addr.addr },
- ip6_mask = { .src_address = ip6_hdr->src_addr.mask,
- .dst_address = ip6_hdr->dst_addr.mask };
+
+ ip6_spec.src_address = ip6_hdr->src_addr.addr;
+ ip6_spec.dst_address = ip6_hdr->dst_addr.addr;
+ ip6_mask.src_address = ip6_hdr->src_addr.mask;
+ ip6_mask.dst_address = ip6_hdr->dst_addr.mask;
item_info[layer].spec = (void *) &ip6_spec;
item_info[layer].mask = (void *) &ip6_mask;
@@ -273,16 +674,15 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
switch (proto)
{
case IP_PROTOCOL_UDP:
- item_info[layer].type = ROC_NPC_ITEM_TYPE_UDP;
-
- udp_header_t udp_spec = { .src_port = l4_src_port,
- .dst_port = l4_dst_port },
- udp_mask = { .src_port = l4_src_mask,
- .dst_port = l4_dst_mask };
+ udp_spec.src_port = l4_src_port;
+ udp_spec.dst_port = l4_dst_port;
+ udp_mask.src_port = l4_src_mask;
+ udp_mask.dst_port = l4_dst_mask;
item_info[layer].spec = (void *) &udp_spec;
item_info[layer].mask = (void *) &udp_mask;
item_info[layer].size = sizeof (udp_header_t);
+ item_info[layer].type = ROC_NPC_ITEM_TYPE_UDP;
layer++;
if (FLOW_IS_L4_TUNNEL_TYPE (flow))
@@ -290,14 +690,13 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
switch (flow->type)
{
case VNET_FLOW_TYPE_IP4_GTPU:
- item_info[layer].type = ROC_NPC_ITEM_TYPE_GTPU;
- gtpu_header_t gtpu_spec = { .teid = clib_host_to_net_u32 (
- flow->ip4_gtpu.teid) },
- gtpu_mask = { .teid = 0XFFFFFFFF };
+ gtpu_spec.teid = clib_host_to_net_u32 (flow->ip4_gtpu.teid);
+ gtpu_mask.teid = 0XFFFFFFFF;
item_info[layer].spec = (void *) &gtpu_spec;
item_info[layer].mask = (void *) &gtpu_mask;
item_info[layer].size = sizeof (gtpu_header_t);
+ item_info[layer].type = ROC_NPC_ITEM_TYPE_GTPU;
layer++;
break;
@@ -309,42 +708,39 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
break;
case IP_PROTOCOL_TCP:
- item_info[layer].type = ROC_NPC_ITEM_TYPE_TCP;
-
- tcp_header_t tcp_spec = { .src_port = l4_src_port,
- .dst_port = l4_dst_port },
- tcp_mask = { .src_port = l4_src_mask,
- .dst_port = l4_dst_mask };
+ tcp_spec.src_port = l4_src_port;
+ tcp_spec.dst_port = l4_dst_port;
+ tcp_mask.src_port = l4_src_mask;
+ tcp_mask.dst_port = l4_dst_mask;
item_info[layer].spec = (void *) &tcp_spec;
item_info[layer].mask = (void *) &tcp_mask;
item_info[layer].size = sizeof (tcp_header_t);
+ item_info[layer].type = ROC_NPC_ITEM_TYPE_TCP;
layer++;
break;
case IP_PROTOCOL_SCTP:
- item_info[layer].type = ROC_NPC_ITEM_TYPE_SCTP;
-
- sctp_header_t sctp_spec = { .src_port = l4_src_port,
- .dst_port = l4_dst_port },
- sctp_mask = { .src_port = l4_src_mask,
- .dst_port = l4_dst_mask };
+ sctp_spec.src_port = l4_src_port;
+ sctp_spec.dst_port = l4_dst_port;
+ sctp_mask.src_port = l4_src_mask;
+ sctp_mask.dst_port = l4_dst_mask;
item_info[layer].spec = (void *) &sctp_spec;
item_info[layer].mask = (void *) &sctp_mask;
item_info[layer].size = sizeof (sctp_header_t);
+ item_info[layer].type = ROC_NPC_ITEM_TYPE_SCTP;
layer++;
break;
case IP_PROTOCOL_IPSEC_ESP:
- item_info[layer].type = ROC_NPC_ITEM_TYPE_ESP;
- esp_header_t esp_spec = { .spi = clib_host_to_net_u32 (
- flow->ip4_ipsec_esp.spi) },
- esp_mask = { .spi = 0xFFFFFFFF };
+ esp_spec.spi = clib_host_to_net_u32 (flow->ip4_ipsec_esp.spi);
+ esp_mask.spi = 0xFFFFFFFF;
item_info[layer].spec = (void *) &esp_spec;
item_info[layer].mask = (void *) &esp_mask;
item_info[layer].size = sizeof (u32);
+ item_info[layer].type = ROC_NPC_ITEM_TYPE_ESP;
layer++;
break;
@@ -357,6 +753,7 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow,
end_item_info:
item_info[layer].type = ROC_NPC_ITEM_TYPE_END;
+parse_flow_actions:
if (flow->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
{
conf.index = flow->redirect_queue;
@@ -422,6 +819,11 @@ end_item_info:
if (queues)
clib_mem_free (queues);
+ vec_free (flow_spec);
+ vec_free (flow_mask);
+ vec_free (drv_spec);
+ vec_free (drv_mask);
+
return rv;
}
diff --git a/src/plugins/dev_octeon/format.c b/src/plugins/dev_octeon/format.c
index e624b84f54e..d0f53013d99 100644
--- a/src/plugins/dev_octeon/format.c
+++ b/src/plugins/dev_octeon/format.c
@@ -25,7 +25,7 @@ format_oct_nix_rx_cqe_desc (u8 *s, va_list *args)
typeof (d->sg0) *sg0 = &d->sg0;
typeof (d->sg0) *sg1 = &d->sg1;
- s = format (s, "hdr: cqe_type %u nude %u q %u tag 0x%x", h->cqe_type,
+ s = format (s, "hdr: cqe_type %u nude %u qid %u tag 0x%x", h->cqe_type,
h->node, h->q, h->tag);
s = format (s, "\n%Uparse:", format_white_space, indent);
#define _(n, f) s = format (s, " " #n " " f, p->n)
diff --git a/src/plugins/dev_octeon/init.c b/src/plugins/dev_octeon/init.c
index 97a11e0d0d7..29d1c16ad96 100644
--- a/src/plugins/dev_octeon/init.c
+++ b/src/plugins/dev_octeon/init.c
@@ -4,7 +4,7 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
+#include <vnet/dev/bus/pci.h>
#include <vnet/dev/counters.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/plugin/plugin.h>
@@ -131,6 +131,9 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev)
.rx_offloads = {
.ip4_cksum = 1,
},
+ .tx_offloads = {
+ .ip4_cksum = 1,
+ },
},
.ops = {
.init = oct_port_init,
@@ -141,6 +144,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev)
.config_change_validate = oct_port_cfg_change_validate,
.format_status = format_oct_port_status,
.format_flow = format_oct_port_flow,
+ .clear_counters = oct_port_clear_counters,
},
.data_size = sizeof (oct_port_t),
.initial_data = &oct_port,
@@ -159,6 +163,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev)
.alloc = oct_rx_queue_alloc,
.free = oct_rx_queue_free,
.format_info = format_oct_rxq_info,
+ .clear_counters = oct_rxq_clear_counters,
},
},
.tx_queue = {
@@ -173,6 +178,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev)
.alloc = oct_tx_queue_alloc,
.free = oct_tx_queue_free,
.format_info = format_oct_txq_info,
+ .clear_counters = oct_txq_clear_counters,
},
},
};
@@ -245,6 +251,7 @@ oct_init (vlib_main_t *vm, vnet_dev_t *dev)
{
case OCT_DEVICE_TYPE_RVU_PF:
case OCT_DEVICE_TYPE_RVU_VF:
+ case OCT_DEVICE_TYPE_LBK_VF:
case OCT_DEVICE_TYPE_SDP_VF:
return oct_init_nix (vm, dev);
diff --git a/src/plugins/dev_octeon/octeon.h b/src/plugins/dev_octeon/octeon.h
index e43cde0a35f..a87a5e3e1ed 100644
--- a/src/plugins/dev_octeon/octeon.h
+++ b/src/plugins/dev_octeon/octeon.h
@@ -12,6 +12,12 @@
#include <vnet/flow/flow.h>
#include <vnet/udp/udp.h>
#include <vnet/ipsec/esp.h>
+#include <vnet/ethernet/packet.h>
+#include <vnet/ip/ip_packet.h>
+#include <vnet/ip/icmp46_packet.h>
+#include <vnet/ip/igmp_packet.h>
+#include <vnet/gre/packet.h>
+#include <vxlan/vxlan.h>
#include <base/roc_api.h>
#include <dev_octeon/hw_defs.h>
@@ -141,6 +147,17 @@ vnet_dev_rv_t oct_flow_validate_params (vlib_main_t *, vnet_dev_port_t *,
vnet_dev_rv_t oct_flow_query (vlib_main_t *, vnet_dev_port_t *, u32, uword,
u64 *);
+/* counter.c */
+void oct_port_add_counters (vlib_main_t *, vnet_dev_port_t *);
+void oct_port_clear_counters (vlib_main_t *, vnet_dev_port_t *);
+void oct_rxq_clear_counters (vlib_main_t *, vnet_dev_rx_queue_t *);
+void oct_txq_clear_counters (vlib_main_t *, vnet_dev_tx_queue_t *);
+vnet_dev_rv_t oct_port_get_stats (vlib_main_t *, vnet_dev_port_t *);
+vnet_dev_rv_t oct_rxq_get_stats (vlib_main_t *, vnet_dev_port_t *,
+ vnet_dev_rx_queue_t *);
+vnet_dev_rv_t oct_txq_get_stats (vlib_main_t *, vnet_dev_port_t *,
+ vnet_dev_tx_queue_t *);
+
#define log_debug(dev, f, ...) \
vlib_log (VLIB_LOG_LEVEL_DEBUG, oct_log.class, "%U: " f, \
format_vnet_dev_addr, (dev), ##__VA_ARGS__)
diff --git a/src/plugins/dev_octeon/port.c b/src/plugins/dev_octeon/port.c
index d5f78301adf..8290880be31 100644
--- a/src/plugins/dev_octeon/port.c
+++ b/src/plugins/dev_octeon/port.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <dev_octeon/octeon.h>
#include <dev_octeon/common.h>
@@ -59,6 +58,7 @@ oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port)
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
oct_port_t *cp = vnet_dev_get_port_data (port);
+ u8 mac_addr[PLT_ETHER_ADDR_LEN];
struct roc_nix *nix = cd->nix;
vnet_dev_rv_t rv;
int rrv;
@@ -76,6 +76,22 @@ oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port)
}
cp->lf_allocated = 1;
+ if (!roc_nix_is_vf_or_sdp (nix))
+ {
+ if ((rrv = roc_nix_npc_mac_addr_get (nix, mac_addr)))
+ {
+ oct_port_deinit (vm, port);
+ return oct_roc_err (dev, rrv, "roc_nix_npc_mac_addr_get failed");
+ }
+
+ /* Sync MAC address to CGX/RPM table */
+ if ((rrv = roc_nix_mac_addr_set (nix, mac_addr)))
+ {
+ oct_port_deinit (vm, port);
+ return oct_roc_err (dev, rrv, "roc_nix_mac_addr_set failed");
+ }
+ }
+
if ((rrv = roc_nix_tm_init (nix)))
{
oct_port_deinit (vm, port);
@@ -124,6 +140,14 @@ oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port)
return rv;
}
+ oct_port_add_counters (vm, port);
+
+ if ((rrv = roc_nix_mac_mtu_set (nix, port->max_rx_frame_size)))
+ {
+ rv = oct_roc_err (dev, rrv, "roc_nix_mac_mtu_set() failed");
+ return rv;
+ }
+
return VNET_DEV_OK;
}
@@ -172,6 +196,21 @@ oct_port_poll (vlib_main_t *vm, vnet_dev_port_t *port)
vnet_dev_port_state_changes_t changes = {};
int rrv;
+ if (oct_port_get_stats (vm, port))
+ return;
+
+ foreach_vnet_dev_port_rx_queue (q, port)
+ {
+ if (oct_rxq_get_stats (vm, port, q))
+ return;
+ }
+
+ foreach_vnet_dev_port_tx_queue (q, port)
+ {
+ if (oct_txq_get_stats (vm, port, q))
+ return;
+ }
+
if (roc_nix_is_lbk (nix))
{
link_info.status = 1;
@@ -203,7 +242,8 @@ oct_port_poll (vlib_main_t *vm, vnet_dev_port_t *port)
if (cd->speed != link_info.speed)
{
changes.change.link_speed = 1;
- changes.link_speed = link_info.speed;
+ /* Convert to Kbps */
+ changes.link_speed = link_info.speed * 1000;
cd->speed = link_info.speed;
}
@@ -327,12 +367,6 @@ oct_port_start (vlib_main_t *vm, vnet_dev_port_t *port)
ctq->n_enq = 0;
}
- if ((rrv = roc_nix_mac_mtu_set (nix, 9200)))
- {
- rv = oct_roc_err (dev, rrv, "roc_nix_mac_mtu_set() failed");
- goto done;
- }
-
if ((rrv = roc_nix_npc_rx_ena_dis (nix, true)))
{
rv = oct_roc_err (dev, rrv, "roc_nix_npc_rx_ena_dis() failed");
@@ -376,6 +410,18 @@ oct_port_stop (vlib_main_t *vm, vnet_dev_port_t *port)
foreach_vnet_dev_port_tx_queue (q, port)
oct_txq_stop (vm, q);
+
+ vnet_dev_port_state_change (vm, port,
+ (vnet_dev_port_state_changes_t){
+ .change.link_state = 1,
+ .change.link_speed = 1,
+ .link_speed = 0,
+ .link_state = 0,
+ });
+
+ /* Update the device status */
+ cd->status = 0;
+ cd->speed = 0;
}
vnet_dev_rv_t
@@ -385,7 +431,7 @@ oct_validate_config_promisc_mode (vnet_dev_port_t *port, int enable)
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
- if (roc_nix_is_vf_or_sdp (nix))
+ if (roc_nix_is_sdp (nix) || roc_nix_is_lbk (nix))
return VNET_DEV_ERR_UNSUPPORTED_DEVICE;
return VNET_DEV_OK;
@@ -405,6 +451,9 @@ oct_op_config_promisc_mode (vlib_main_t *vm, vnet_dev_port_t *port, int enable)
return oct_roc_err (dev, rv, "roc_nix_npc_promisc_ena_dis failed");
}
+ if (!roc_nix_is_pf (nix))
+ return VNET_DEV_OK;
+
rv = roc_nix_mac_promisc_mode_enable (nix, enable);
if (rv)
{
@@ -416,6 +465,61 @@ oct_op_config_promisc_mode (vlib_main_t *vm, vnet_dev_port_t *port, int enable)
return VNET_DEV_OK;
}
+static vnet_dev_rv_t
+oct_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port,
+ vnet_dev_hw_addr_t *addr, int is_add,
+ int is_primary)
+{
+ vnet_dev_t *dev = port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ i32 rrv;
+
+ if (is_primary)
+ {
+ if (is_add)
+ {
+ /* Update mac address at NPC */
+ rrv = roc_nix_npc_mac_addr_set (nix, (u8 *) addr);
+ if (rrv)
+ rv = oct_roc_err (dev, rrv, "roc_nix_npc_mac_addr_set() failed");
+
+ /* Update mac address at CGX for PFs only */
+ if (!roc_nix_is_vf_or_sdp (nix))
+ {
+ rrv = roc_nix_mac_addr_set (nix, (u8 *) addr);
+ if (rrv)
+ {
+ /* Rollback to previous mac address */
+ roc_nix_npc_mac_addr_set (nix,
+ (u8 *) &port->primary_hw_addr);
+ rv = oct_roc_err (dev, rrv, "roc_nix_mac_addr_set() failed");
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+vnet_dev_rv_t
+oct_op_config_max_rx_len (vlib_main_t *vm, vnet_dev_port_t *port,
+ u32 rx_frame_size)
+{
+ vnet_dev_t *dev = port->dev;
+ oct_device_t *cd = vnet_dev_get_data (dev);
+ struct roc_nix *nix = cd->nix;
+ vnet_dev_rv_t rv = VNET_DEV_OK;
+ i32 rrv;
+
+ rrv = roc_nix_mac_max_rx_len_set (nix, rx_frame_size);
+ if (rrv)
+ rv = oct_roc_err (dev, rrv, "roc_nix_mac_max_rx_len_set() failed");
+
+ return rv;
+}
+
vnet_dev_rv_t
oct_port_cfg_change_validate (vlib_main_t *vm, vnet_dev_port_t *port,
vnet_dev_port_cfg_change_req_t *req)
@@ -465,6 +569,9 @@ oct_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port,
break;
case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR:
+ rv = oct_port_add_del_eth_addr (vm, port, &req->addr,
+ /* is_add */ 1,
+ /* is_primary */ 1);
break;
case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
@@ -474,6 +581,7 @@ oct_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port,
break;
case VNET_DEV_PORT_CFG_MAX_RX_FRAME_SIZE:
+ rv = oct_op_config_max_rx_len (vm, port, req->max_rx_frame_size);
break;
case VNET_DEV_PORT_CFG_ADD_RX_FLOW:
diff --git a/src/plugins/dev_octeon/queue.c b/src/plugins/dev_octeon/queue.c
index d6ae794fb8d..58d391b8508 100644
--- a/src/plugins/dev_octeon/queue.c
+++ b/src/plugins/dev_octeon/queue.c
@@ -4,7 +4,6 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <dev_octeon/octeon.h>
#include <vnet/ethernet/ethernet.h>
diff --git a/src/plugins/dev_octeon/roc_helper.c b/src/plugins/dev_octeon/roc_helper.c
index f10c2cb578b..16e0a871a9d 100644
--- a/src/plugins/dev_octeon/roc_helper.c
+++ b/src/plugins/dev_octeon/roc_helper.c
@@ -49,6 +49,12 @@ oct_plt_get_thread_index (void)
return __os_thread_index;
}
+static u64
+oct_plt_get_cache_line_size (void)
+{
+ return CLIB_CACHE_LINE_BYTES;
+}
+
static void
oct_drv_physmem_free (vlib_main_t *vm, void *mem)
{
@@ -178,4 +184,5 @@ oct_plt_init_param_t oct_plt_init_param = {
.oct_plt_spinlock_unlock = oct_plt_spinlock_unlock,
.oct_plt_spinlock_trylock = oct_plt_spinlock_trylock,
.oct_plt_get_thread_index = oct_plt_get_thread_index,
+ .oct_plt_get_cache_line_size = oct_plt_get_cache_line_size,
};
diff --git a/src/plugins/dev_octeon/rx_node.c b/src/plugins/dev_octeon/rx_node.c
index 997f1356199..1f8d5d93fa3 100644
--- a/src/plugins/dev_octeon/rx_node.c
+++ b/src/plugins/dev_octeon/rx_node.c
@@ -165,6 +165,38 @@ oct_rx_batch (vlib_main_t *vm, oct_rx_node_ctx_t *ctx,
return n;
}
+#ifdef PLATFORM_OCTEON9
+static_always_inline u32
+oct_rxq_refill (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq, u16 n_refill)
+{
+ u32 n_alloc, n_free;
+ u32 buffer_indices[n_refill];
+ vlib_buffer_t *buffers[n_refill];
+ u8 bpi = vnet_dev_get_rx_queue_buffer_pool_index (rxq);
+ oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq);
+ u64 aura = roc_npa_aura_handle_to_aura (crq->aura_handle);
+ const uint64_t addr =
+ roc_npa_aura_handle_to_base (crq->aura_handle) + NPA_LF_AURA_OP_FREE0;
+
+ if (n_refill < 256)
+ return 0;
+
+ n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_refill);
+ if (PREDICT_FALSE (n_alloc < n_refill))
+ goto alloc_fail;
+
+ vlib_get_buffers (vm, buffer_indices, (vlib_buffer_t **) buffers, n_alloc);
+
+ for (n_free = 0; n_free < n_alloc; n_free++)
+ roc_store_pair ((u64) buffers[n_free], aura, addr);
+
+ return n_alloc;
+
+alloc_fail:
+ vlib_buffer_unalloc_to_pool (vm, buffer_indices, n_alloc, bpi);
+ return 0;
+}
+#else
static_always_inline void
oct_rxq_refill_batch (vlib_main_t *vm, u64 lmt_id, u64 addr,
oct_npa_lf_aura_batch_free_line_t *lines, u32 *bi,
@@ -260,6 +292,7 @@ oct_rxq_refill (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq, u16 n_refill)
return n_enq;
}
+#endif
static_always_inline void
oct_rx_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
diff --git a/src/plugins/dev_octeon/tx_node.c b/src/plugins/dev_octeon/tx_node.c
index a2e4b07de8a..f42f18d989b 100644
--- a/src/plugins/dev_octeon/tx_node.c
+++ b/src/plugins/dev_octeon/tx_node.c
@@ -32,6 +32,44 @@ typedef struct
lmt_line_t *lmt_lines;
} oct_tx_ctx_t;
+#ifdef PLATFORM_OCTEON9
+static_always_inline u32
+oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq)
+{
+ oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
+ u16 off = ctq->hdr_off;
+ u64 ah = ctq->aura_handle;
+ u32 n_freed = 0, n;
+
+ ah = ctq->aura_handle;
+
+ if ((n = roc_npa_aura_op_available (ah)) >= 32)
+ {
+ u64 buffers[n];
+ u32 bi[n];
+
+ n_freed = roc_npa_aura_op_bulk_alloc (ah, buffers, n, 0, 1);
+ vlib_get_buffer_indices_with_offset (vm, (void **) &buffers, bi, n_freed,
+ off);
+ vlib_buffer_free_no_next (vm, bi, n_freed);
+ }
+
+ return n_freed;
+}
+
+static_always_inline void
+oct_lmt_copy (void *lmt_addr, u64 io_addr, void *desc, u64 dwords)
+{
+ u64 lmt_status;
+
+ do
+ {
+ roc_lmt_mov_seg (lmt_addr, desc, dwords);
+ lmt_status = roc_lmt_submit_ldeor (io_addr);
+ }
+ while (lmt_status == 0);
+}
+#else
static_always_inline u32
oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq)
{
@@ -133,6 +171,7 @@ oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq)
return n_freed;
}
+#endif
static_always_inline u8
oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b,
@@ -158,6 +197,11 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b,
return 0;
}
+#ifdef PLATFORM_OCTEON9
+ /* Override line for Octeon9 */
+ line = ctx->lmt_lines;
+#endif
+
if (!simple && flags & VLIB_BUFFER_NEXT_PRESENT)
{
u8 n_tail_segs = 0;
@@ -211,19 +255,18 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b,
if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM)
{
d.hdr_w1.ol3type = NIX_SENDL3TYPE_IP4_CKSUM;
- d.hdr_w1.ol3ptr = vnet_buffer (b)->l3_hdr_offset;
- d.hdr_w1.ol4ptr =
- vnet_buffer (b)->l3_hdr_offset + sizeof (ip4_header_t);
+ d.hdr_w1.ol3ptr = vnet_buffer (b)->l3_hdr_offset - b->current_data;
+ d.hdr_w1.ol4ptr = d.hdr_w1.ol3ptr + sizeof (ip4_header_t);
}
if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)
{
d.hdr_w1.ol4type = NIX_SENDL4TYPE_UDP_CKSUM;
- d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset;
+ d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset - b->current_data;
}
else if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)
{
d.hdr_w1.ol4type = NIX_SENDL4TYPE_TCP_CKSUM;
- d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset;
+ d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset - b->current_data;
}
}
@@ -238,8 +281,12 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b,
t->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
}
+#ifdef PLATFORM_OCTEON9
+ oct_lmt_copy (line, ctx->lmt_ioaddr, &d, n_dwords);
+#else
for (u32 i = 0; i < n_dwords; i++)
line->dwords[i] = d.as_u128[i];
+#endif
*dpl = n_dwords;
*n = *n + 1;
@@ -252,8 +299,9 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq,
vlib_buffer_t **b, u32 n_pkts, int trace)
{
u8 dwords_per_line[16], *dpl = dwords_per_line;
- u64 lmt_arg, ioaddr, n_lines;
- u32 n_left, or_flags_16 = 0, n = 0;
+ u64 __attribute__ ((unused)) lmt_arg, ioaddr, n_lines;
+ u32 __attribute__ ((unused)) or_flags_16 = 0;
+ u32 n_left, n = 0;
const u32 not_simple_flags =
VLIB_BUFFER_NEXT_PRESENT | VNET_BUFFER_F_OFFLOAD;
lmt_line_t *l = ctx->lmt_lines;
@@ -331,6 +379,7 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq,
if (PREDICT_FALSE (!n_lines))
return n_pkts;
+#ifndef PLATFORM_OCTEON9
if (PREDICT_FALSE (or_flags_16 & VLIB_BUFFER_NEXT_PRESENT))
{
dpl = dwords_per_line;
@@ -359,6 +408,7 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq,
}
roc_lmt_submit_steorl (lmt_arg, ioaddr);
+#endif
return n_pkts;
}
@@ -375,7 +425,11 @@ VNET_DEV_NODE_FN (oct_tx_node)
u32 *from = vlib_frame_vector_args (frame);
u32 n, n_enq, n_left, n_pkts = frame->n_vectors;
vlib_buffer_t *buffers[VLIB_FRAME_SIZE + 8], **b = buffers;
+#ifdef PLATFORM_OCTEON9
+ u64 lmt_id = 0;
+#else
u64 lmt_id = vm->thread_index << ROC_LMT_LINES_PER_CORE_LOG2;
+#endif
oct_tx_ctx_t ctx = {
.node = node,
diff --git a/src/plugins/dpdk/device/dpdk.h b/src/plugins/dpdk/device/dpdk.h
index 88a4d9ff618..a069fbe3818 100644
--- a/src/plugins/dpdk/device/dpdk.h
+++ b/src/plugins/dpdk/device/dpdk.h
@@ -131,6 +131,7 @@ typedef struct
u32 interface_number_from_port_id : 1;
u32 use_intel_phdr_cksum : 1;
u32 int_unmaskable : 1;
+ vlib_simple_counter_main_t *xstats_counters;
} dpdk_driver_t;
dpdk_driver_t *dpdk_driver_find (const char *name, const char **desc);
@@ -240,6 +241,7 @@ typedef struct
_ (num_rx_desc) \
_ (num_tx_desc) \
_ (max_lro_pkt_size) \
+ _ (disable_rxq_int) \
_ (rss_fn)
typedef enum
diff --git a/src/plugins/dpdk/device/dpdk_priv.h b/src/plugins/dpdk/device/dpdk_priv.h
index cb7b185c112..e5b5a35df80 100644
--- a/src/plugins/dpdk/device/dpdk_priv.h
+++ b/src/plugins/dpdk/device/dpdk_priv.h
@@ -47,14 +47,18 @@ dpdk_device_flag_set (dpdk_device_t *xd, __typeof__ (xd->flags) flag, int val)
xd->flags = val ? xd->flags | flag : xd->flags & ~flag;
}
+void dpdk_counters_xstats_init (dpdk_device_t *xd);
+
static inline void
-dpdk_get_xstats (dpdk_device_t * xd)
+dpdk_get_xstats (dpdk_device_t *xd, u32 thread_index)
{
- int len, ret;
-
+ int ret;
+ int i;
+ int len;
if (!(xd->flags & DPDK_DEVICE_FLAG_ADMIN_UP))
return;
-
+ if (xd->driver == 0)
+ return;
len = rte_eth_xstats_get (xd->port_id, NULL, 0);
if (len < 0)
return;
@@ -64,11 +68,26 @@ dpdk_get_xstats (dpdk_device_t * xd)
ret = rte_eth_xstats_get (xd->port_id, xd->xstats, len);
if (ret < 0 || ret > len)
{
+ /* Failed, expand vector and try again on next time around the track. */
+ vec_validate (xd->xstats, ret - 1);
vec_set_len (xd->xstats, 0);
+ dpdk_log_warn ("rte_eth_xstats_get(%d) failed: %d", xd->port_id, ret);
return;
}
-
- vec_set_len (xd->xstats, len);
+ if (len == vec_len (xd->driver->xstats_counters))
+ {
+ vec_foreach_index (i, xd->xstats)
+ {
+ vlib_set_simple_counter (&xd->driver->xstats_counters[i],
+ thread_index, xd->sw_if_index,
+ xd->xstats[i].value);
+ }
+ }
+ else
+ {
+ dpdk_log_warn ("rte_eth_xstats_get vector size mismatch (%d/%d", len,
+ vec_len (xd->driver->xstats_counters));
+ }
}
#define DPDK_UPDATE_COUNTER(vnm, tidx, xd, stat, cnt) \
@@ -107,7 +126,7 @@ dpdk_update_counters (dpdk_device_t * xd, f64 now)
DPDK_UPDATE_COUNTER (vnm, thread_index, xd, ierrors,
VNET_INTERFACE_COUNTER_RX_ERROR);
- dpdk_get_xstats (xd);
+ dpdk_get_xstats (xd, thread_index);
}
#if RTE_VERSION < RTE_VERSION_NUM(21, 11, 0, 0)
diff --git a/src/plugins/dpdk/device/init.c b/src/plugins/dpdk/device/init.c
index 421f662efa2..827f8801ca6 100644
--- a/src/plugins/dpdk/device/init.c
+++ b/src/plugins/dpdk/device/init.c
@@ -30,7 +30,7 @@
#include <dpdk/cryptodev/cryptodev.h>
#include <vlib/pci/pci.h>
#include <vlib/vmbus/vmbus.h>
-
+#include <vlib/stats/stats.h>
#include <rte_ring.h>
#include <rte_vect.h>
@@ -226,6 +226,75 @@ dpdk_find_startup_config (struct rte_eth_dev_info *di)
return &dm->conf->default_devconf;
}
+/*
+ * Initialise or refresh the xstats counters for a device
+ */
+void
+dpdk_counters_xstats_init (dpdk_device_t *xd)
+{
+ int len, ret, i;
+ struct rte_eth_xstat_name *xstats_names = 0;
+ char *name;
+ dpdk_driver_t *dr = xd->driver;
+
+ /* Only support xstats for supported drivers */
+ if (!dr)
+ return;
+
+ len = rte_eth_xstats_get_names (xd->port_id, 0, 0);
+ if (len < 0)
+ {
+ dpdk_log_err ("[%u] rte_eth_xstats_get_names failed: %d", xd->port_id,
+ len);
+ return;
+ }
+ /* Counters for this driver is already initialised */
+ if (vec_len (dr->xstats_counters) == len)
+ {
+ vec_foreach_index (i, dr->xstats_counters)
+ {
+ vlib_validate_simple_counter (&dr->xstats_counters[i],
+ xd->sw_if_index);
+ vlib_zero_simple_counter (&dr->xstats_counters[i], xd->sw_if_index);
+ }
+ return;
+ }
+
+ /* Same driver, different interface, different length of counter array. */
+ ASSERT (vec_len (dr->xstats_counters) == 0);
+
+ vec_validate (xstats_names, len - 1);
+
+ ret = rte_eth_xstats_get_names (xd->port_id, xstats_names, len);
+ if (ret >= 0 && ret <= len)
+ {
+ vec_validate (dr->xstats_counters, len - 1);
+ vec_foreach_index (i, xstats_names)
+ {
+ name = (char *) format (0, "/if/%s/%s%c", dr->drivers->name,
+ xstats_names[i].name, 0);
+
+ /* There is a bug in the ENA driver where the xstats names are not
+ * unique. */
+ if (vlib_stats_find_entry_index (name) != STAT_SEGMENT_INDEX_INVALID)
+ {
+ vec_free (name);
+ name = (char *) format (0, "/if/%s/%s_%d%c", dr->drivers->name,
+ xstats_names[i].name, i, 0);
+ }
+
+ dr->xstats_counters[i].name = name;
+ dr->xstats_counters[i].stat_segment_name = name;
+ dr->xstats_counters[i].counters = 0;
+ vlib_validate_simple_counter (&dr->xstats_counters[i],
+ xd->sw_if_index);
+ vlib_zero_simple_counter (&dr->xstats_counters[i], xd->sw_if_index);
+ vec_free (name);
+ }
+ }
+ vec_free (xstats_names);
+}
+
static clib_error_t *
dpdk_lib_init (dpdk_main_t * dm)
{
@@ -519,6 +588,9 @@ dpdk_lib_init (dpdk_main_t * dm)
if (devconf->max_lro_pkt_size)
xd->conf.max_lro_pkt_size = devconf->max_lro_pkt_size;
+ if (devconf->disable_rxq_int)
+ xd->conf.enable_rxq_int = 0;
+
dpdk_device_setup (xd);
/* rss queues should be configured after dpdk_device_setup() */
@@ -532,6 +604,7 @@ dpdk_lib_init (dpdk_main_t * dm)
if (vec_len (xd->errors))
dpdk_log_err ("[%u] setup failed Errors:\n %U", port_id,
format_dpdk_device_errors, xd);
+ dpdk_counters_xstats_init (xd);
}
for (int i = 0; i < vec_len (dm->devices); i++)
@@ -936,6 +1009,10 @@ dpdk_device_config (dpdk_config_main_t *conf, void *addr,
if (error)
break;
}
+ else if (unformat (input, "no-rx-interrupts"))
+ {
+ devconf->disable_rxq_int = 1;
+ }
else if (unformat (input, "tso on"))
{
devconf->tso = DPDK_DEVICE_TSO_ON;
@@ -1045,19 +1122,21 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input)
dpdk_main_t *dm = &dpdk_main;
clib_error_t *error = 0;
dpdk_config_main_t *conf = &dpdk_config_main;
- vlib_thread_main_t *tm = vlib_get_thread_main ();
dpdk_device_config_t *devconf;
vlib_pci_addr_t pci_addr = { 0 };
vlib_vmbus_addr_t vmbus_addr = { 0 };
unformat_input_t sub_input;
+#ifdef __linux
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
uword default_hugepage_sz, x;
+ u8 file_prefix = 0;
+#endif /* __linux__ */
u8 *s, *tmp = 0;
int ret, i;
int num_whitelisted = 0;
int eal_no_hugetlb = 0;
u8 no_pci = 0;
u8 no_vmbus = 0;
- u8 file_prefix = 0;
u8 *socket_mem = 0;
u32 vendor, device, domain, bus, func;
void *fmt_func;
@@ -1217,6 +1296,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input)
}
foreach_eal_double_hyphen_predicate_arg
#undef _
+#ifdef __linux__
#define _(a) \
else if (unformat(input, #a " %s", &s)) \
{ \
@@ -1232,6 +1312,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input)
}
foreach_eal_double_hyphen_arg
#undef _
+#endif /* __linux__ */
#define _(a,b) \
else if (unformat(input, #a " %s", &s)) \
{ \
@@ -1258,6 +1339,11 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input)
{
vec_add1 (conf->eal_init_args, (u8 *) "--in-memory");
+#ifdef __linux__
+ /*
+ * FreeBSD performs huge page prealloc through a dedicated kernel mode
+ * this process is only required on Linux.
+ */
default_hugepage_sz = clib_mem_get_default_hugepage_size ();
clib_bitmap_foreach (x, tm->cpu_socket_bitmap)
@@ -1272,6 +1358,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input)
if ((e = clib_sysfs_prealloc_hugepages(x, 0, n_pages)))
clib_error_report (e);
}
+#endif /* __linux__ */
}
/* on/off dpdk's telemetry thread */
diff --git a/src/plugins/flowprobe/flowprobe.c b/src/plugins/flowprobe/flowprobe.c
index 58a7cfe22f1..ee0a8eb8a31 100644
--- a/src/plugins/flowprobe/flowprobe.c
+++ b/src/plugins/flowprobe/flowprobe.c
@@ -48,7 +48,7 @@ uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = {
.arc_name = "ip4-unicast",
.node_name = "flowprobe-input-ip4",
- .runs_before = VNET_FEATURES ("ip4-lookup"),
+ .runs_before = VNET_FEATURES ("ip4-lookup", "ip4-inacl"),
};
VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = {
.arc_name = "ip4-multicast",
@@ -58,7 +58,7 @@ VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = {
VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = {
.arc_name = "ip6-unicast",
.node_name = "flowprobe-input-ip6",
- .runs_before = VNET_FEATURES ("ip6-lookup"),
+ .runs_before = VNET_FEATURES ("ip6-lookup", "ip6-inacl"),
};
VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = {
.arc_name = "ip6-multicast",
diff --git a/src/plugins/hs_apps/CMakeLists.txt b/src/plugins/hs_apps/CMakeLists.txt
index 179c9c7a4c4..ba03e393f44 100644
--- a/src/plugins/hs_apps/CMakeLists.txt
+++ b/src/plugins/hs_apps/CMakeLists.txt
@@ -21,8 +21,10 @@ add_vpp_plugin(hs_apps
hs_apps.c
http_cli.c
http_client_cli.c
+ http_simple_post.c
http_tps.c
proxy.c
+ test_builtins.c
)
##############################################################################
diff --git a/src/plugins/hs_apps/echo_client.c b/src/plugins/hs_apps/echo_client.c
index d1443e75e80..8dec5d86824 100644
--- a/src/plugins/hs_apps/echo_client.c
+++ b/src/plugins/hs_apps/echo_client.c
@@ -429,8 +429,11 @@ ec_init (vlib_main_t *vm)
ecm->app_is_init = 1;
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
vlib_worker_thread_barrier_sync (vm);
- vnet_session_enable_disable (vm, 1 /* turn on session and transports */);
+ vnet_session_enable_disable (vm, &args);
/* Turn on the builtin client input nodes */
foreach_vlib_main ()
diff --git a/src/plugins/hs_apps/echo_server.c b/src/plugins/hs_apps/echo_server.c
index 0243252434a..756a1cc3451 100644
--- a/src/plugins/hs_apps/echo_server.c
+++ b/src/plugins/hs_apps/echo_server.c
@@ -736,7 +736,10 @@ echo_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
goto cleanup;
}
- vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
if (!server_uri_set)
{
diff --git a/src/plugins/hs_apps/http_cli.c b/src/plugins/hs_apps/http_cli.c
index 5d4d49c0fba..e0bb9afba4a 100644
--- a/src/plugins/hs_apps/http_cli.c
+++ b/src/plugins/hs_apps/http_cli.c
@@ -17,12 +17,23 @@
#include <vnet/session/application_interface.h>
#include <vnet/session/session.h>
#include <http/http.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+
+#define HCS_DEBUG 0
+
+#if HCS_DEBUG
+#define HCS_DBG(_fmt, _args...) clib_warning (_fmt, ##_args)
+#else
+#define HCS_DBG(_fmt, _args...)
+#endif
typedef struct
{
u32 hs_index;
u32 thread_index;
u64 node_index;
+ u8 plain_text;
u8 *buf;
} hcs_cli_args_t;
@@ -34,6 +45,7 @@ typedef struct
u8 *tx_buf;
u32 tx_offset;
u32 vpp_session_index;
+ http_header_t *resp_headers;
} hcs_session_t;
typedef struct
@@ -143,26 +155,48 @@ start_send_data (hcs_session_t *hs, http_status_code_t status)
{
http_msg_t msg;
session_t *ts;
+ u8 *headers_buf = 0;
int rv;
+ if (vec_len (hs->resp_headers))
+ {
+ headers_buf = http_serialize_headers (hs->resp_headers);
+ vec_free (hs->resp_headers);
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = vec_len (headers_buf);
+ }
+ else
+ {
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = 0;
+ }
+
msg.type = HTTP_MSG_REPLY;
msg.code = status;
- msg.content_type = HTTP_CONTENT_TEXT_HTML;
msg.data.type = HTTP_MSG_DATA_INLINE;
- msg.data.len = vec_len (hs->tx_buf);
+ msg.data.body_len = vec_len (hs->tx_buf);
+ msg.data.body_offset = msg.data.headers_len;
+ msg.data.len = msg.data.body_len + msg.data.headers_len;
ts = session_get (hs->vpp_session_index, hs->thread_index);
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
- if (!msg.data.len)
+ if (msg.data.headers_len)
+ {
+ rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf);
+ ASSERT (rv == msg.data.headers_len);
+ vec_free (headers_buf);
+ }
+
+ if (!msg.data.body_len)
goto done;
rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (hs->tx_buf), hs->tx_buf);
if (rv != vec_len (hs->tx_buf))
{
- hs->tx_offset = rv;
+ hs->tx_offset = (rv > 0) ? rv : 0;
svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
}
else
@@ -173,7 +207,7 @@ start_send_data (hcs_session_t *hs, http_status_code_t status)
done:
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
}
static void
@@ -181,6 +215,7 @@ send_data_to_http (void *rpc_args)
{
hcs_cli_args_t *args = (hcs_cli_args_t *) rpc_args;
hcs_session_t *hs;
+ http_content_type_t type = HTTP_CONTENT_TEXT_HTML;
hs = hcs_session_get (args->thread_index, args->hs_index);
if (!hs)
@@ -190,6 +225,13 @@ send_data_to_http (void *rpc_args)
}
hs->tx_buf = args->buf;
+ if (args->plain_text)
+ type = HTTP_CONTENT_TEXT_PLAIN;
+
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (type));
+
start_send_data (hs, HTTP_STATUS_OK);
cleanup:
@@ -218,17 +260,9 @@ hcs_cli_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
{
if (request[i] == '/')
request[i] = ' ';
- else if (request[i] == ' ')
- {
- /* vlib_cli_input is vector-based, no need for a NULL */
- vec_set_len (request, i);
- break;
- }
i++;
}
-
- /* Generate the html header */
- html = format (0, html_header_template, request /* title */ );
+ HCS_DBG ("%v", request);
/* Run the command */
unformat_init_vector (&input, vec_dup (request));
@@ -236,9 +270,17 @@ hcs_cli_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
unformat_free (&input);
request = 0;
- /* Generate the html page */
- html = format (html, "%v", reply);
- html = format (html, html_footer);
+ if (args->plain_text)
+ {
+ html = format (0, "%v", reply);
+ }
+ else
+ {
+ /* Generate the html page */
+ html = format (0, html_header_template, request /* title */);
+ html = format (html, "%v", reply);
+ html = format (html, html_footer);
+ }
/* Send it */
rpc_args = clib_mem_alloc (sizeof (*args));
@@ -308,9 +350,11 @@ hcs_ts_rx_callback (session_t *ts)
hcs_cli_args_t args = {};
hcs_session_t *hs;
http_msg_t msg;
- int rv;
+ int rv, is_encoded = 0;
hs = hcs_session_get (ts->thread_index, ts->opaque);
+ hs->tx_buf = 0;
+ hs->resp_headers = 0;
/* Read the http message header */
rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
@@ -318,16 +362,66 @@ hcs_ts_rx_callback (session_t *ts)
if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
{
- hs->tx_buf = 0;
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_ALLOW),
+ http_token_lit ("GET"));
start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
- return 0;
+ goto done;
+ }
+
+ if (msg.data.target_path_len == 0 ||
+ msg.data.target_form != HTTP_TARGET_ORIGIN_FORM)
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
}
/* send the command to a new/recycled vlib process */
- vec_validate (args.buf, msg.data.len - 1);
- rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, args.buf);
- ASSERT (rv == msg.data.len);
- vec_set_len (args.buf, rv);
+ vec_validate (args.buf, msg.data.target_path_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset,
+ msg.data.target_path_len, args.buf);
+ ASSERT (rv == msg.data.target_path_len);
+ HCS_DBG ("%v", args.buf);
+ if (http_validate_abs_path_syntax (args.buf, &is_encoded))
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ vec_free (args.buf);
+ goto done;
+ }
+ if (is_encoded)
+ {
+ u8 *decoded = http_percent_decode (args.buf);
+ vec_free (args.buf);
+ args.buf = decoded;
+ }
+
+ if (msg.data.headers_len)
+ {
+ u8 *headers = 0;
+ http_header_table_t *ht;
+ vec_validate (headers, msg.data.headers_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
+ msg.data.headers_len, headers);
+ ASSERT (rv == msg.data.headers_len);
+ if (http_parse_headers (headers, &ht))
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ vec_free (args.buf);
+ vec_free (headers);
+ goto done;
+ }
+ const char *accept_value =
+ http_get_header (ht, http_header_name_str (HTTP_HEADER_ACCEPT));
+ if (accept_value)
+ {
+ HCS_DBG ("client accept: %s", accept_value);
+ /* just for testing purpose, we don't care about precedence */
+ if (strstr (accept_value, "text/plain"))
+ args.plain_text = 1;
+ }
+ http_free_header_table (ht);
+ vec_free (headers);
+ }
args.hs_index = hs->session_index;
args.thread_index = ts->thread_index;
@@ -338,6 +432,9 @@ hcs_ts_rx_callback (session_t *ts)
sizeof (args));
else
alloc_cli_process (&args);
+
+done:
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len);
return 0;
}
@@ -372,7 +469,7 @@ hcs_ts_tx_callback (session_t *ts)
}
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
return 0;
}
@@ -634,7 +731,10 @@ start_server:
if (hcm->app_index != (u32) ~0)
return clib_error_return (0, "test http server is already running");
- vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
rv = hcs_create (vm);
switch (rv)
diff --git a/src/plugins/hs_apps/http_client_cli.c b/src/plugins/hs_apps/http_client_cli.c
index 1a321bf44a8..4b8ef173bd9 100644
--- a/src/plugins/hs_apps/http_client_cli.c
+++ b/src/plugins/hs_apps/http_client_cli.c
@@ -16,6 +16,9 @@
#include <vnet/session/application_interface.h>
#include <vnet/session/session.h>
#include <http/http.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+#include <http/http_status_codes.h>
#define HCC_DEBUG 0
@@ -32,14 +35,14 @@ typedef struct
u32 thread_index;
u32 rx_offset;
u32 vpp_session_index;
- u32 to_recv;
+ u64 to_recv;
u8 is_closed;
+ http_header_t *req_headers;
} hcc_session_t;
typedef struct
{
hcc_session_t *sessions;
- u8 *rx_buf;
u32 thread_index;
} hcc_worker_t;
@@ -67,6 +70,7 @@ typedef enum
{
HCC_REPLY_RECEIVED = 100,
HCC_TRANSPORT_CLOSED,
+ HCC_CONNECT_FAILED,
} hcc_cli_signal_t;
static hcc_main_t hcc_main;
@@ -94,13 +98,6 @@ hcc_session_get (u32 hs_index, u32 thread_index)
return pool_elt_at_index (wrk->sessions, hs_index);
}
-static void
-hcc_session_free (u32 thread_index, hcc_session_t *hs)
-{
- hcc_worker_t *wrk = hcc_worker_get (thread_index);
- pool_put (wrk->sessions, hs);
-}
-
static int
hcc_ts_accept_callback (session_t *ts)
{
@@ -127,6 +124,7 @@ hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as,
hcc_session_t *hs, *new_hs;
hcc_worker_t *wrk;
http_msg_t msg;
+ u8 *headers_buf;
int rv;
HCC_DBG ("hc_index: %d", hc_index);
@@ -135,6 +133,8 @@ hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as,
{
clib_warning ("connected error: hc_index(%d): %U", hc_index,
format_session_error, err);
+ vlib_process_signal_event_mt (hcm->vlib_main, hcm->cli_node_index,
+ HCC_CONNECT_FAILED, 0);
return -1;
}
@@ -146,24 +146,42 @@ hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as,
hs->vpp_session_index = as->session_index;
+ http_add_header (&hs->req_headers,
+ http_header_name_token (HTTP_HEADER_ACCEPT),
+ http_content_type_token (HTTP_CONTENT_TEXT_HTML));
+ headers_buf = http_serialize_headers (hs->req_headers);
+ vec_free (hs->req_headers);
+
msg.type = HTTP_MSG_REQUEST;
msg.method_type = HTTP_REQ_GET;
- msg.content_type = HTTP_CONTENT_TEXT_HTML;
+ /* request target */
+ msg.data.target_form = HTTP_TARGET_ORIGIN_FORM;
+ msg.data.target_path_offset = 0;
+ msg.data.target_path_len = vec_len (hcm->http_query);
+ /* custom headers */
+ msg.data.headers_offset = msg.data.target_path_len;
+ msg.data.headers_len = vec_len (headers_buf);
+ /* request body */
+ msg.data.body_len = 0;
+ /* data type and total length */
msg.data.type = HTTP_MSG_DATA_INLINE;
- msg.data.len = vec_len (hcm->http_query);
+ msg.data.len =
+ msg.data.target_path_len + msg.data.headers_len + msg.data.body_len;
- svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
- { hcm->http_query, vec_len (hcm->http_query) } };
+ svm_fifo_seg_t segs[3] = { { (u8 *) &msg, sizeof (msg) },
+ { hcm->http_query, vec_len (hcm->http_query) },
+ { headers_buf, vec_len (headers_buf) } };
- rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 2, 0 /* allow partial */);
- if (rv < 0 || rv != sizeof (msg) + vec_len (hcm->http_query))
+ rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 3, 0 /* allow partial */);
+ vec_free (headers_buf);
+ if (rv < 0 || rv != sizeof (msg) + msg.data.len)
{
clib_warning ("failed app enqueue");
return -1;
}
if (svm_fifo_set_event (as->tx_fifo))
- session_send_io_evt_to_thread (as->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (as->handle, SESSION_IO_EVT_TX);
return 0;
}
@@ -218,23 +236,32 @@ hcc_ts_rx_callback (session_t *ts)
if (hs->to_recv == 0)
{
+ /* read the http message header */
rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
- if (msg.type != HTTP_MSG_REPLY || msg.code != HTTP_STATUS_OK)
+ if (msg.type != HTTP_MSG_REPLY)
{
clib_warning ("unexpected msg type %d", msg.type);
return 0;
}
- vec_validate (hcm->http_response, msg.data.len - 1);
+ /* drop everything up to body */
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset);
+ hs->to_recv = msg.data.body_len;
+ if (msg.code != HTTP_STATUS_OK && hs->to_recv == 0)
+ {
+ hcm->http_response = format (0, "request failed, response code: %U",
+ format_http_status_code, msg.code);
+ goto done;
+ }
+ vec_validate (hcm->http_response, msg.data.body_len - 1);
vec_reset_length (hcm->http_response);
- hs->to_recv = msg.data.len;
}
u32 max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
u32 n_deq = clib_min (hs->to_recv, max_deq);
- u32 curr = vec_len (hcm->http_response);
+ u64 curr = vec_len (hcm->http_response);
rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, hcm->http_response + curr);
if (rv < 0)
{
@@ -248,10 +275,12 @@ hcc_ts_rx_callback (session_t *ts)
vec_set_len (hcm->http_response, curr + n_deq);
ASSERT (hs->to_recv >= rv);
hs->to_recv -= rv;
- HCC_DBG ("app rcvd %d, remains %d", rv, hs->to_recv);
+ HCC_DBG ("app rcvd %d, remains %llu", rv, hs->to_recv);
+done:
if (hs->to_recv == 0)
{
+ HCC_DBG ("all data received, going to disconnect");
hcc_session_disconnect (ts);
vlib_process_signal_event_mt (hcm->vlib_main, hcm->cli_node_index,
HCC_REPLY_RECEIVED, 0);
@@ -261,18 +290,6 @@ hcc_ts_rx_callback (session_t *ts)
}
static void
-hcc_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
-{
- hcc_session_t *hs;
-
- hs = hcc_session_get (s->thread_index, s->opaque);
- if (!hs)
- return;
-
- hcc_session_free (s->thread_index, hs);
-}
-
-static void
hcc_ts_transport_closed (session_t *s)
{
hcc_main_t *hcm = &hcc_main;
@@ -290,7 +307,6 @@ static session_cb_vft_t hcc_session_cb_vft = {
.builtin_app_rx_callback = hcc_ts_rx_callback,
.builtin_app_tx_callback = hcc_ts_tx_callback,
.session_reset_callback = hcc_ts_reset_callback,
- .session_cleanup_callback = hcc_ts_cleanup_callback,
.session_transport_closed_callback = hcc_ts_transport_closed,
};
@@ -420,11 +436,13 @@ hcc_run (vlib_main_t *vm, int print_output)
case HCC_REPLY_RECEIVED:
if (print_output)
vlib_cli_output (vm, "%v", hcm->http_response);
- vec_free (hcm->http_response);
break;
case HCC_TRANSPORT_CLOSED:
err = clib_error_return (0, "error, transport closed");
break;
+ case HCC_CONNECT_FAILED:
+ err = clib_error_return (0, "failed to connect");
+ break;
default:
err = clib_error_return (0, "unexpected event %d", event_type);
break;
@@ -454,6 +472,28 @@ hcc_detach ()
return rv;
}
+static void
+hcc_worker_cleanup (hcc_worker_t *wrk)
+{
+ pool_free (wrk->sessions);
+}
+
+static void
+hcc_cleanup ()
+{
+ hcc_main_t *hcm = &hcc_main;
+ hcc_worker_t *wrk;
+
+ vec_foreach (wrk, hcm->wrk)
+ hcc_worker_cleanup (wrk);
+
+ vec_free (hcm->uri);
+ vec_free (hcm->http_query);
+ vec_free (hcm->http_response);
+ vec_free (hcm->appns_id);
+ vec_free (hcm->wrk);
+}
+
static clib_error_t *
hcc_command_fn (vlib_main_t *vm, unformat_input_t *input,
vlib_cli_command_t *cmd)
@@ -503,7 +543,6 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input,
}
}
- vec_free (hcm->appns_id);
hcm->appns_id = appns_id;
hcm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index;
@@ -519,8 +558,11 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input,
goto done;
}
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
vlib_worker_thread_barrier_sync (vm);
- vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */);
+ vnet_session_enable_disable (vm, &args);
vlib_worker_thread_barrier_release (vm);
err = hcc_run (vm, print_output);
@@ -534,8 +576,7 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input,
}
done:
- vec_free (hcm->uri);
- vec_free (hcm->http_query);
+ hcc_cleanup ();
unformat_free (line_input);
return err;
}
diff --git a/src/plugins/hs_apps/http_simple_post.c b/src/plugins/hs_apps/http_simple_post.c
new file mode 100644
index 00000000000..6212eac1c97
--- /dev/null
+++ b/src/plugins/hs_apps/http_simple_post.c
@@ -0,0 +1,581 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vnet/session/application.h>
+#include <vnet/session/application_interface.h>
+#include <vnet/session/session.h>
+#include <http/http.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+#include <vppinfra/unix.h>
+
+typedef struct
+{
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+ u32 session_index;
+ u32 thread_index;
+ u32 vpp_session_index;
+ u8 is_closed;
+} hsp_session_t;
+
+typedef struct
+{
+ hsp_session_t *sessions;
+ u32 thread_index;
+} hsp_worker_t;
+
+typedef struct
+{
+ u32 app_index;
+ vlib_main_t *vlib_main;
+ u32 cli_node_index;
+ u8 attached;
+ u8 *uri;
+ session_endpoint_cfg_t connect_sep;
+ u8 *target;
+ u8 *headers_buf;
+ u8 *data;
+ u64 data_offset;
+ hsp_worker_t *wrk;
+ u8 *http_response;
+ u8 is_file;
+ u8 use_ptr;
+} hsp_main_t;
+
+typedef enum
+{
+ HSP_CONNECT_FAILED = 1,
+ HSP_TRANSPORT_CLOSED,
+ HSP_REPLY_RECEIVED,
+} hsp_cli_signal_t;
+
+static hsp_main_t hsp_main;
+
+static inline hsp_worker_t *
+hsp_worker_get (u32 thread_index)
+{
+ return &hsp_main.wrk[thread_index];
+}
+
+static inline hsp_session_t *
+hsp_session_get (u32 session_index, u32 thread_index)
+{
+ hsp_worker_t *wrk = hsp_worker_get (thread_index);
+ return pool_elt_at_index (wrk->sessions, session_index);
+}
+
+static hsp_session_t *
+hsp_session_alloc (hsp_worker_t *wrk)
+{
+ hsp_session_t *s;
+
+ pool_get_zero (wrk->sessions, s);
+ s->session_index = s - wrk->sessions;
+ s->thread_index = wrk->thread_index;
+
+ return s;
+}
+
+static int
+hsp_session_connected_callback (u32 app_index, u32 hsp_session_index,
+ session_t *s, session_error_t err)
+{
+ hsp_main_t *hspm = &hsp_main;
+ hsp_session_t *hsp_session, *new_hsp_session;
+ hsp_worker_t *wrk;
+ http_header_t *headers = 0;
+ http_msg_t msg;
+ u64 to_send;
+ u32 n_enq;
+ int rv;
+
+ if (err)
+ {
+ clib_warning ("hsp_session_index[%d] connected error: %U",
+ hsp_session_index, format_session_error, err);
+ vlib_process_signal_event_mt (hspm->vlib_main, hspm->cli_node_index,
+ HSP_CONNECT_FAILED, 0);
+ return -1;
+ }
+
+ hsp_session = hsp_session_get (hsp_session_index, 0);
+ wrk = hsp_worker_get (s->thread_index);
+ new_hsp_session = hsp_session_alloc (wrk);
+ clib_memcpy_fast (new_hsp_session, hsp_session, sizeof (*hsp_session));
+ hsp_session->vpp_session_index = s->session_index;
+
+ if (hspm->is_file)
+ {
+ http_add_header (
+ &headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (HTTP_CONTENT_APP_OCTET_STREAM));
+ }
+ else
+ {
+ http_add_header (
+ &headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (HTTP_CONTENT_APP_X_WWW_FORM_URLENCODED));
+ }
+ hspm->headers_buf = http_serialize_headers (headers);
+ vec_free (headers);
+
+ msg.type = HTTP_MSG_REQUEST;
+ msg.method_type = HTTP_REQ_POST;
+ /* request target */
+ msg.data.target_form = HTTP_TARGET_ORIGIN_FORM;
+ msg.data.target_path_len = vec_len (hspm->target);
+ /* custom headers */
+ msg.data.headers_len = vec_len (hspm->headers_buf);
+ /* request body */
+ msg.data.body_len = vec_len (hspm->data);
+ /* total length */
+ msg.data.len =
+ msg.data.target_path_len + msg.data.headers_len + msg.data.body_len;
+
+ if (hspm->use_ptr)
+ {
+ uword target = pointer_to_uword (hspm->target);
+ uword headers = pointer_to_uword (hspm->headers_buf);
+ uword body = pointer_to_uword (hspm->data);
+ msg.data.type = HTTP_MSG_DATA_PTR;
+ svm_fifo_seg_t segs[4] = {
+ { (u8 *) &msg, sizeof (msg) },
+ { (u8 *) &target, sizeof (target) },
+ { (u8 *) &headers, sizeof (headers) },
+ { (u8 *) &body, sizeof (body) },
+ };
+
+ rv =
+ svm_fifo_enqueue_segments (s->tx_fifo, segs, 4, 0 /* allow partial */);
+ ASSERT (rv == (sizeof (msg) + sizeof (target) + sizeof (headers) +
+ sizeof (body)));
+ goto done;
+ }
+
+ msg.data.type = HTTP_MSG_DATA_INLINE;
+ msg.data.target_path_offset = 0;
+ msg.data.headers_offset = msg.data.target_path_len;
+ msg.data.body_offset = msg.data.headers_offset + msg.data.headers_len;
+
+ rv = svm_fifo_enqueue (s->tx_fifo, sizeof (msg), (u8 *) &msg);
+ ASSERT (rv == sizeof (msg));
+
+ rv = svm_fifo_enqueue (s->tx_fifo, vec_len (hspm->target), hspm->target);
+ ASSERT (rv == vec_len (hspm->target));
+
+ rv = svm_fifo_enqueue (s->tx_fifo, vec_len (hspm->headers_buf),
+ hspm->headers_buf);
+ ASSERT (rv == msg.data.headers_len);
+
+ to_send = vec_len (hspm->data);
+ n_enq = clib_min (svm_fifo_size (s->tx_fifo), to_send);
+
+ rv = svm_fifo_enqueue (s->tx_fifo, n_enq, hspm->data);
+
+ if (rv < to_send)
+ {
+ hspm->data_offset = (rv > 0) ? rv : 0;
+ svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ }
+
+done:
+ if (svm_fifo_set_event (s->tx_fifo))
+ session_program_tx_io_evt (s->handle, SESSION_IO_EVT_TX);
+
+ return 0;
+}
+
+static void
+hsp_session_disconnect_callback (session_t *s)
+{
+ hsp_main_t *hspm = &hsp_main;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+ int rv;
+
+ a->handle = session_handle (s);
+ a->app_index = hspm->app_index;
+ if ((rv = vnet_disconnect_session (a)))
+ clib_warning ("warning: disconnect returned: %U", format_session_error,
+ rv);
+}
+
+static void
+hsp_session_transport_closed_callback (session_t *s)
+{
+ hsp_main_t *hspm = &hsp_main;
+
+ vlib_process_signal_event_mt (hspm->vlib_main, hspm->cli_node_index,
+ HSP_TRANSPORT_CLOSED, 0);
+}
+
+static void
+hsp_session_reset_callback (session_t *s)
+{
+ hsp_main_t *hspm = &hsp_main;
+ hsp_session_t *hsp_session;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+ int rv;
+
+ hsp_session = hsp_session_get (s->opaque, s->thread_index);
+ hsp_session->is_closed = 1;
+
+ a->handle = session_handle (s);
+ a->app_index = hspm->app_index;
+ if ((rv = vnet_disconnect_session (a)))
+ clib_warning ("warning: disconnect returned: %U", format_session_error,
+ rv);
+}
+
+static int
+hsp_rx_callback (session_t *s)
+{
+ hsp_main_t *hspm = &hsp_main;
+ hsp_session_t *hsp_session;
+ http_msg_t msg;
+ int rv;
+
+ hsp_session = hsp_session_get (s->opaque, s->thread_index);
+
+ if (hsp_session->is_closed)
+ {
+ clib_warning ("hsp_session_index[%d] is closed", s->opaque);
+ return -1;
+ }
+
+ rv = svm_fifo_dequeue (s->rx_fifo, sizeof (msg), (u8 *) &msg);
+ ASSERT (rv == sizeof (msg));
+
+ if (msg.type != HTTP_MSG_REPLY)
+ {
+ clib_warning ("unexpected msg type %d", msg.type);
+ return -1;
+ }
+
+ svm_fifo_dequeue_drop_all (s->rx_fifo);
+
+ if (msg.code == HTTP_STATUS_OK)
+ hspm->http_response = format (0, "request success");
+ else
+ hspm->http_response = format (0, "request failed");
+
+ hsp_session_disconnect_callback (s);
+ vlib_process_signal_event_mt (hspm->vlib_main, hspm->cli_node_index,
+ HSP_REPLY_RECEIVED, 0);
+ return 0;
+}
+
+static int
+hsp_tx_callback (session_t *s)
+{
+ hsp_main_t *hspm = &hsp_main;
+ u64 to_send;
+ u32 n_enq;
+ int rv;
+
+ to_send = vec_len (hspm->data) - hspm->data_offset;
+ n_enq = clib_min (svm_fifo_size (s->tx_fifo), to_send);
+
+ rv = svm_fifo_enqueue (s->tx_fifo, n_enq, hspm->data + hspm->data_offset);
+
+ if (rv <= 0)
+ {
+ svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ return 0;
+ }
+
+ if (rv < to_send)
+ {
+ hspm->data_offset += rv;
+ svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ }
+
+ if (svm_fifo_set_event (s->tx_fifo))
+ session_program_tx_io_evt (s->handle, SESSION_IO_EVT_TX);
+
+ return 0;
+}
+
+static session_cb_vft_t hsp_session_cb_vft = {
+ .session_connected_callback = hsp_session_connected_callback,
+ .session_disconnect_callback = hsp_session_disconnect_callback,
+ .session_transport_closed_callback = hsp_session_transport_closed_callback,
+ .session_reset_callback = hsp_session_reset_callback,
+ .builtin_app_rx_callback = hsp_rx_callback,
+ .builtin_app_tx_callback = hsp_tx_callback,
+};
+
+static clib_error_t *
+hsp_attach ()
+{
+ hsp_main_t *hspm = &hsp_main;
+ vnet_app_attach_args_t _a, *a = &_a;
+ u64 options[18];
+ int rv;
+
+ clib_memset (a, 0, sizeof (*a));
+ clib_memset (options, 0, sizeof (options));
+
+ a->api_client_index = APP_INVALID_INDEX;
+ a->name = format (0, "http_simple_post");
+ a->session_cb_vft = &hsp_session_cb_vft;
+ a->options = options;
+ a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
+
+ if ((rv = vnet_application_attach (a)))
+ return clib_error_return (0, "attach returned: %U", format_session_error,
+ rv);
+
+ hspm->app_index = a->app_index;
+ vec_free (a->name);
+ hspm->attached = 1;
+
+ return 0;
+}
+
+static int
+hsp_connect_rpc (void *rpc_args)
+{
+ vnet_connect_args_t *a = rpc_args;
+ int rv;
+
+ rv = vnet_connect (a);
+ if (rv)
+ clib_warning (0, "connect returned: %U", format_session_error, rv);
+
+ vec_free (a);
+ return rv;
+}
+
+static void
+hsp_connect ()
+{
+ hsp_main_t *hspm = &hsp_main;
+ vnet_connect_args_t *a = 0;
+ hsp_worker_t *wrk;
+ hsp_session_t *hsp_session;
+
+ vec_validate (a, 0);
+ clib_memset (a, 0, sizeof (a[0]));
+
+ clib_memcpy (&a->sep_ext, &hspm->connect_sep, sizeof (hspm->connect_sep));
+ a->app_index = hspm->app_index;
+
+ /* allocate http session on main thread */
+ wrk = hsp_worker_get (0);
+ hsp_session = hsp_session_alloc (wrk);
+ a->api_context = hsp_session->session_index;
+
+ session_send_rpc_evt_to_thread_force (transport_cl_thread (),
+ hsp_connect_rpc, a);
+}
+
+static clib_error_t *
+hsp_run (vlib_main_t *vm)
+{
+ hsp_main_t *hspm = &hsp_main;
+ vlib_thread_main_t *vtm = vlib_get_thread_main ();
+ u32 num_threads;
+ hsp_worker_t *wrk;
+ uword event_type, *event_data = 0;
+ clib_error_t *err;
+
+ num_threads = 1 /* main thread */ + vtm->n_threads;
+ vec_validate (hspm->wrk, num_threads);
+ vec_foreach (wrk, hspm->wrk)
+ wrk->thread_index = wrk - hspm->wrk;
+
+ if ((err = hsp_attach ()))
+ return clib_error_return (0, "http simple post attach: %U",
+ format_clib_error, err);
+
+ hsp_connect ();
+
+ vlib_process_wait_for_event_or_clock (vm, 10);
+ event_type = vlib_process_get_events (vm, &event_data);
+ switch (event_type)
+ {
+ case ~0:
+ err = clib_error_return (0, "error: timeout");
+ break;
+ case HSP_CONNECT_FAILED:
+ err = clib_error_return (0, "error: failed to connect");
+ break;
+ case HSP_TRANSPORT_CLOSED:
+ err = clib_error_return (0, "error: transport closed");
+ break;
+ case HSP_REPLY_RECEIVED:
+ vlib_cli_output (vm, "%v", hspm->http_response);
+ break;
+ default:
+ err = clib_error_return (0, "error: unexpected event %d", event_type);
+ break;
+ }
+
+ vec_free (event_data);
+ return err;
+}
+
+static int
+hsp_detach ()
+{
+ hsp_main_t *hspm = &hsp_main;
+ vnet_app_detach_args_t _da, *da = &_da;
+ int rv;
+
+ if (!hspm->attached)
+ return 0;
+
+ da->app_index = hspm->app_index;
+ da->api_client_index = APP_INVALID_INDEX;
+ rv = vnet_application_detach (da);
+ hspm->attached = 0;
+ hspm->app_index = APP_INVALID_INDEX;
+
+ return rv;
+}
+
+static void
+hcc_worker_cleanup (hsp_worker_t *wrk)
+{
+ pool_free (wrk->sessions);
+}
+
+static void
+hsp_cleanup ()
+{
+ hsp_main_t *hspm = &hsp_main;
+ hsp_worker_t *wrk;
+
+ vec_foreach (wrk, hspm->wrk)
+ hcc_worker_cleanup (wrk);
+
+ vec_free (hspm->uri);
+ vec_free (hspm->target);
+ vec_free (hspm->headers_buf);
+ vec_free (hspm->data);
+ vec_free (hspm->http_response);
+ vec_free (hspm->wrk);
+}
+
+static clib_error_t *
+hsp_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ hsp_main_t *hspm = &hsp_main;
+ clib_error_t *err = 0;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u8 *path = 0;
+ u8 *file_data;
+ int rv;
+
+ if (hspm->attached)
+ return clib_error_return (0, "failed: already running!");
+
+ hspm->use_ptr = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "expected required arguments");
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "uri %s", &hspm->uri))
+ ;
+ else if (unformat (line_input, "data %v", &hspm->data))
+ hspm->is_file = 0;
+ else if (unformat (line_input, "target %s", &hspm->target))
+ ;
+ else if (unformat (line_input, "file %s", &path))
+ hspm->is_file = 1;
+ else if (unformat (line_input, "use-ptr"))
+ hspm->use_ptr = 1;
+ else
+ {
+ err = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ if (!hspm->uri)
+ {
+ err = clib_error_return (0, "URI not defined");
+ goto done;
+ }
+ if (!hspm->target)
+ {
+ err = clib_error_return (0, "target not defined");
+ goto done;
+ }
+ if (!hspm->data)
+ {
+ if (path)
+ {
+ err = clib_file_contents ((char *) path, &file_data);
+ if (err)
+ goto done;
+ hspm->data = file_data;
+ }
+ else
+ {
+ err = clib_error_return (0, "data not defined");
+ goto done;
+ }
+ }
+
+ if ((rv = parse_uri ((char *) hspm->uri, &hspm->connect_sep)))
+ {
+ err =
+ clib_error_return (0, "URI parse error: %U", format_session_error, rv);
+ goto done;
+ }
+
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vlib_worker_thread_barrier_sync (vm);
+ vnet_session_enable_disable (vm, &args);
+ vlib_worker_thread_barrier_release (vm);
+
+ hspm->cli_node_index =
+ vlib_get_current_process (vm)->node_runtime.node_index;
+
+ err = hsp_run (vm);
+
+ if ((rv = hsp_detach ()))
+ {
+ /* don't override last error */
+ if (!err)
+ err = clib_error_return (0, "detach returned: %U",
+ format_session_error, rv);
+ else
+ clib_warning ("warning: detach returned: %U", format_session_error,
+ rv);
+ }
+
+done:
+ hsp_cleanup ();
+ unformat_free (line_input);
+ return err;
+}
+
+VLIB_CLI_COMMAND (hsp_command, static) = {
+ .path = "http post",
+ .short_help = "uri http://<ip-addr> target <origin-form> "
+ "[data <form-urlencoded> | file <file-path>] [use-ptr]",
+ .function = hsp_command_fn,
+ .is_mp_safe = 1,
+};
+
+static clib_error_t *
+hsp_main_init (vlib_main_t *vm)
+{
+ hsp_main_t *hspm = &hsp_main;
+
+ hspm->app_index = APP_INVALID_INDEX;
+ hspm->vlib_main = vm;
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (hsp_main_init);
diff --git a/src/plugins/hs_apps/http_tps.c b/src/plugins/hs_apps/http_tps.c
index 920f7ea731f..cdeafa5d54a 100644
--- a/src/plugins/hs_apps/http_tps.c
+++ b/src/plugins/hs_apps/http_tps.c
@@ -17,6 +17,10 @@
#include <vnet/session/application_interface.h>
#include <vnet/session/session.h>
#include <http/http.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+
+#define HTS_RX_BUF_SIZE (64 << 10)
typedef struct
{
@@ -26,6 +30,8 @@ typedef struct
u64 data_len;
u64 data_offset;
u32 vpp_session_index;
+ u64 left_recv;
+ u64 total_recv;
union
{
/** threshold after which connection is closed */
@@ -34,6 +40,8 @@ typedef struct
u32 close_rate;
};
u8 *uri;
+ u8 *rx_buf;
+ http_header_t *resp_headers;
} hts_session_t;
typedef struct hts_listen_cfg_
@@ -102,6 +110,8 @@ hts_session_free (hts_session_t *hs)
if (htm->debug_level > 0)
clib_warning ("Freeing session %u", hs->session_index);
+ vec_free (hs->rx_buf);
+
if (CLIB_DEBUG)
clib_memset (hs, 0xfa, sizeof (*hs));
@@ -151,7 +161,7 @@ hts_session_tx_zc (hts_session_t *hs, session_t *ts)
svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
}
static void
@@ -198,7 +208,7 @@ hts_session_tx_no_zc (hts_session_t *hs, session_t *ts)
svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
}
static inline void
@@ -223,22 +233,46 @@ hts_start_send_data (hts_session_t *hs, http_status_code_t status)
{
http_msg_t msg;
session_t *ts;
+ u8 *headers_buf = 0;
+ u32 n_segs = 1;
+ svm_fifo_seg_t seg[2];
int rv;
+ if (vec_len (hs->resp_headers))
+ {
+ headers_buf = http_serialize_headers (hs->resp_headers);
+ vec_free (hs->resp_headers);
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = vec_len (headers_buf);
+ seg[1].data = headers_buf;
+ seg[1].len = msg.data.headers_len;
+ n_segs = 2;
+ }
+ else
+ {
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = 0;
+ }
+
msg.type = HTTP_MSG_REPLY;
msg.code = status;
- msg.content_type = HTTP_CONTENT_APP_OCTET_STREAM;
msg.data.type = HTTP_MSG_DATA_INLINE;
- msg.data.len = hs->data_len;
+ msg.data.body_len = hs->data_len;
+ msg.data.body_offset = msg.data.headers_len;
+ msg.data.len = msg.data.body_len + msg.data.headers_len;
+ seg[0].data = (u8 *) &msg;
+ seg[0].len = sizeof (msg);
ts = session_get (hs->vpp_session_index, hs->thread_index);
- rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
- ASSERT (rv == sizeof (msg));
+ rv = svm_fifo_enqueue_segments (ts->tx_fifo, seg, n_segs,
+ 0 /* allow partial */);
+ vec_free (headers_buf);
+ ASSERT (rv == (sizeof (msg) + msg.data.headers_len));
- if (!msg.data.len)
+ if (!msg.data.body_len)
{
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
return;
}
@@ -246,7 +280,7 @@ hts_start_send_data (hts_session_t *hs, http_status_code_t status)
}
static int
-try_test_file (hts_session_t *hs, u8 *request)
+try_test_file (hts_session_t *hs, u8 *target)
{
char *test_str = "test_file";
hts_main_t *htm = &hts_main;
@@ -254,10 +288,10 @@ try_test_file (hts_session_t *hs, u8 *request)
uword file_size;
int rc = 0;
- if (memcmp (request, test_str, clib_strnlen (test_str, 9)))
+ if (memcmp (target, test_str, clib_strnlen (test_str, 9)))
return -1;
- unformat_init_vector (&input, vec_dup (request));
+ unformat_init_vector (&input, vec_dup (target));
if (!unformat (&input, "test_file_%U", unformat_memory_size, &file_size))
{
rc = -1;
@@ -286,6 +320,10 @@ try_test_file (hts_session_t *hs, u8 *request)
}
}
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (HTTP_CONTENT_APP_OCTET_STREAM));
+
hts_start_send_data (hs, HTTP_STATUS_OK);
done:
@@ -294,39 +332,121 @@ done:
return rc;
}
+static inline void
+hts_session_rx_body (hts_session_t *hs, session_t *ts)
+{
+ hts_main_t *htm = &hts_main;
+ u32 n_deq;
+ int rv;
+
+ n_deq = svm_fifo_max_dequeue (ts->rx_fifo);
+ if (!htm->no_zc)
+ {
+ svm_fifo_dequeue_drop_all (ts->rx_fifo);
+ }
+ else
+ {
+ n_deq = clib_min (n_deq, HTS_RX_BUF_SIZE);
+ rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, hs->rx_buf);
+ ASSERT (rv == n_deq);
+ }
+ hs->left_recv -= n_deq;
+
+ if (hs->close_threshold > 0)
+ {
+ if ((f64) (hs->total_recv - hs->left_recv) / hs->total_recv >
+ hs->close_threshold)
+ hts_disconnect_transport (hs);
+ }
+
+ if (hs->left_recv == 0)
+ {
+ hts_start_send_data (hs, HTTP_STATUS_OK);
+ vec_free (hs->rx_buf);
+ }
+}
+
static int
hts_ts_rx_callback (session_t *ts)
{
+ hts_main_t *htm = &hts_main;
hts_session_t *hs;
- u8 *request = 0;
+ u8 *target = 0;
http_msg_t msg;
int rv;
hs = hts_session_get (ts->thread_index, ts->opaque);
- /* Read the http message header */
- rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
- ASSERT (rv == sizeof (msg));
-
- if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
+ if (hs->left_recv == 0)
{
- hts_start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
- goto done;
- }
+ hs->data_len = 0;
+ hs->resp_headers = 0;
+ hs->rx_buf = 0;
- if (!msg.data.len)
- {
- hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
- goto done;
- }
+ /* Read the http message header */
+ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
+ ASSERT (rv == sizeof (msg));
+
+ if (msg.type != HTTP_MSG_REQUEST)
+ {
+ hts_start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR);
+ goto done;
+ }
+ if (msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST)
+ {
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_ALLOW),
+ http_token_lit ("GET, POST"));
+ hts_start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
+ goto done;
+ }
- vec_validate (request, msg.data.len - 1);
- rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request);
+ if (msg.data.target_path_len == 0 ||
+ msg.data.target_form != HTTP_TARGET_ORIGIN_FORM)
+ {
+ hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
+ }
- if (try_test_file (hs, request))
- hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND);
+ vec_validate (target, msg.data.target_path_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset,
+ msg.data.target_path_len, target);
+ ASSERT (rv == msg.data.target_path_len);
-done:
+ if (htm->debug_level)
+ clib_warning ("%s request target: %v",
+ msg.method_type == HTTP_REQ_GET ? "GET" : "POST",
+ target);
+
+ if (msg.method_type == HTTP_REQ_GET)
+ {
+ if (try_test_file (hs, target))
+ hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND);
+ vec_free (target);
+ }
+ else
+ {
+ vec_free (target);
+ if (!msg.data.body_len)
+ {
+ hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
+ }
+ /* drop everything up to body */
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset);
+ hs->left_recv = msg.data.body_len;
+ hs->total_recv = msg.data.body_len;
+ if (htm->no_zc)
+ vec_validate (hs->rx_buf, HTS_RX_BUF_SIZE - 1);
+ hts_session_rx_body (hs, ts);
+ return 0;
+ }
+
+ done:
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len);
+ }
+ else
+ hts_session_rx_body (hs, ts);
return 0;
}
@@ -354,6 +474,7 @@ hts_ts_accept_callback (session_t *ts)
hs = hts_session_alloc (ts->thread_index);
hs->vpp_session_index = ts->session_index;
+ hs->left_recv = 0;
ts->opaque = hs->session_index;
ts->session_state = SESSION_STATE_READY;
@@ -717,7 +838,10 @@ start_server:
if (htm->app_index == (u32) ~0)
{
- vnet_session_enable_disable (vm, 1 /* is_enable */);
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
if (hts_create (vm))
{
diff --git a/src/plugins/hs_apps/proxy.c b/src/plugins/hs_apps/proxy.c
index e8fedf921a5..c7e7b2a653c 100644
--- a/src/plugins/hs_apps/proxy.c
+++ b/src/plugins/hs_apps/proxy.c
@@ -368,6 +368,13 @@ proxy_rx_callback (session_t * s)
u32 max_dequeue, ps_index;
int actual_transfer __attribute__ ((unused));
+ /* maybe we were already here */
+ if (ps->active_open_establishing)
+ {
+ clib_spinlock_unlock_if_init (&pm->sessions_lock);
+ return 0;
+ }
+
rx_fifo = s->rx_fifo;
tx_fifo = s->tx_fifo;
@@ -418,12 +425,12 @@ static void
proxy_force_ack (void *handlep)
{
transport_connection_t *tc;
- session_t *ao_s;
+ session_t *s;
- ao_s = session_get_from_handle (pointer_to_uword (handlep));
- if (session_get_transport_proto (ao_s) != TRANSPORT_PROTO_TCP)
+ s = session_get_from_handle (pointer_to_uword (handlep));
+ if (session_get_transport_proto (s) != TRANSPORT_PROTO_TCP)
return;
- tc = session_get_transport (ao_s);
+ tc = session_get_transport (s);
tcp_send_ack ((tcp_connection_t *) tc);
}
@@ -619,9 +626,7 @@ static int
active_open_tx_callback (session_t * ao_s)
{
proxy_main_t *pm = &proxy_main;
- transport_connection_t *tc;
proxy_session_t *ps;
- session_t *proxy_s;
u32 min_free;
min_free = clib_min (svm_fifo_size (ao_s->tx_fifo) >> 3, 128 << 10);
@@ -637,14 +642,13 @@ active_open_tx_callback (session_t * ao_s)
if (!ps)
goto unlock;
- if (ps->vpp_server_handle == ~0)
+ if (ps->vpp_server_handle == SESSION_INVALID_HANDLE)
goto unlock;
- proxy_s = session_get_from_handle (ps->vpp_server_handle);
-
/* Force ack on proxy side to update rcv wnd */
- tc = session_get_transport (proxy_s);
- tcp_send_ack ((tcp_connection_t *) tc);
+ void *arg = uword_to_pointer (ps->vpp_server_handle, void *);
+ session_send_rpc_evt_to_thread (
+ session_thread_from_handle (ps->vpp_server_handle), proxy_force_ack, arg);
unlock:
clib_spinlock_unlock_if_init (&pm->sessions_lock);
@@ -915,7 +919,10 @@ proxy_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
goto done;
}
- vnet_session_enable_disable (vm, 1 /* turn on session and transport */ );
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
rv = proxy_server_create (vm);
switch (rv)
diff --git a/src/plugins/hs_apps/test_builtins.c b/src/plugins/hs_apps/test_builtins.c
new file mode 100644
index 00000000000..631c1f1a8a2
--- /dev/null
+++ b/src/plugins/hs_apps/test_builtins.c
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#include <http_static/http_static.h>
+#include <vppinfra/tw_timer_2t_1w_2048sl.h>
+
+typedef struct
+{
+ u32 stop_timer_handle;
+ hss_session_handle_t sh;
+} tw_timer_elt_t;
+
+typedef struct tb_main_
+{
+ tw_timer_elt_t *delayed_resps;
+ tw_timer_wheel_2t_1w_2048sl_t tw;
+ hss_session_send_fn send_data;
+} tb_main_t;
+
+static tb_main_t tb_main;
+
+static uword
+test_builtins_timer_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
+ vlib_frame_t *f)
+{
+ tb_main_t *tbm = &tb_main;
+ f64 now, timeout = 1.0;
+ uword *event_data = 0;
+ uword __clib_unused event_type;
+
+ while (1)
+ {
+ vlib_process_wait_for_event_or_clock (vm, timeout);
+ now = vlib_time_now (vm);
+ event_type = vlib_process_get_events (vm, (uword **) &event_data);
+
+ /* expire timers */
+ tw_timer_expire_timers_2t_1w_2048sl (&tbm->tw, now);
+
+ vec_reset_length (event_data);
+ }
+ return 0;
+}
+
+VLIB_REGISTER_NODE (test_builtins_timer_process_node) = {
+ .function = test_builtins_timer_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "test-builtins-timer-process",
+ .state = VLIB_NODE_STATE_DISABLED,
+};
+
+static void
+send_data_to_hss (hss_session_handle_t sh, u8 *data)
+{
+ tb_main_t *tbm = &tb_main;
+ hss_url_handler_args_t args = {};
+
+ args.sh = sh;
+ args.data = data;
+ args.data_len = vec_len (data);
+ args.ct = HTTP_CONTENT_TEXT_PLAIN;
+ args.sc = HTTP_STATUS_OK;
+ args.free_vec_data = 1;
+
+ tbm->send_data (&args);
+}
+
+static hss_url_handler_rc_t
+handle_get_test1 (hss_url_handler_args_t *args)
+{
+ u8 *data;
+
+ clib_warning ("get request on test1");
+ data = format (0, "hello");
+ send_data_to_hss (args->sh, data);
+
+ return HSS_URL_HANDLER_ASYNC;
+}
+
+static hss_url_handler_rc_t
+handle_get_test2 (hss_url_handler_args_t *args)
+{
+ u8 *data;
+
+ clib_warning ("get request on test2");
+ data = format (0, "some data");
+ send_data_to_hss (args->sh, data);
+
+ return HSS_URL_HANDLER_ASYNC;
+}
+
+static void
+delayed_resp_cb (u32 *expired_timers)
+{
+ tb_main_t *tbm = &tb_main;
+ int i;
+ u32 pool_index;
+ tw_timer_elt_t *e;
+ u8 *data;
+
+ for (i = 0; i < vec_len (expired_timers); i++)
+ {
+ pool_index = expired_timers[i] & 0x7FFFFFFF;
+ e = pool_elt_at_index (tbm->delayed_resps, pool_index);
+ clib_warning ("sending delayed data");
+ data = format (0, "delayed data");
+ send_data_to_hss (e->sh, data);
+ pool_put (tbm->delayed_resps, e);
+ }
+}
+
+static hss_url_handler_rc_t
+handle_get_test_delayed (hss_url_handler_args_t *args)
+{
+ tb_main_t *tbm = &tb_main;
+ tw_timer_elt_t *e;
+
+ clib_warning ("get request on test_delayed");
+ pool_get (tbm->delayed_resps, e);
+ e->sh = args->sh;
+ e->stop_timer_handle =
+ tw_timer_start_2t_1w_2048sl (&tbm->tw, e - tbm->delayed_resps, 0, 5);
+
+ return HSS_URL_HANDLER_ASYNC;
+}
+
+static hss_url_handler_rc_t
+handle_post_test3 (hss_url_handler_args_t *args)
+{
+ send_data_to_hss (args->sh, 0);
+ return HSS_URL_HANDLER_ASYNC;
+}
+
+static void
+test_builtins_init (vlib_main_t *vm)
+{
+ tb_main_t *tbm = &tb_main;
+ hss_register_url_fn fp;
+ vlib_node_t *n;
+
+ fp = vlib_get_plugin_symbol ("http_static_plugin.so",
+ "hss_register_url_handler");
+
+ if (fp == 0)
+ {
+ clib_warning ("http_static_plugin.so not loaded...");
+ return;
+ }
+
+ (*fp) (handle_get_test1, "test1", HTTP_REQ_GET);
+ (*fp) (handle_get_test2, "test2", HTTP_REQ_GET);
+ (*fp) (handle_get_test_delayed, "test_delayed", HTTP_REQ_GET);
+ (*fp) (handle_post_test3, "test3", HTTP_REQ_POST);
+
+ tbm->send_data =
+ vlib_get_plugin_symbol ("http_static_plugin.so", "hss_session_send_data");
+
+ tw_timer_wheel_init_2t_1w_2048sl (&tbm->tw, delayed_resp_cb, 1.0, ~0);
+
+ vlib_node_set_state (vm, test_builtins_timer_process_node.index,
+ VLIB_NODE_STATE_POLLING);
+ n = vlib_get_node (vm, test_builtins_timer_process_node.index);
+ vlib_start_process (vm, n->runtime_index);
+}
+
+static clib_error_t *
+test_builtins_enable_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ test_builtins_init (vm);
+ return 0;
+}
+
+VLIB_CLI_COMMAND (test_builtins_enable_command, static) = {
+ .path = "test-url-handler enable",
+ .short_help = "test-url-handler enable",
+ .function = test_builtins_enable_command_fn,
+};
diff --git a/src/plugins/hs_apps/vcl/vcl_test.h b/src/plugins/hs_apps/vcl/vcl_test.h
index 0ce27ef84e2..11667fb144a 100644
--- a/src/plugins/hs_apps/vcl/vcl_test.h
+++ b/src/plugins/hs_apps/vcl/vcl_test.h
@@ -124,7 +124,7 @@ typedef struct
typedef struct
{
- const vcl_test_proto_vft_t *protos[VPPCOM_PROTO_SRTP + 1];
+ const vcl_test_proto_vft_t *protos[VPPCOM_PROTO_HTTP + 1];
uint32_t ckpair_index;
hs_test_cfg_t cfg;
vcl_test_wrk_t *wrk;
@@ -420,6 +420,39 @@ vcl_test_write (vcl_test_session_t *ts, void *buf, uint32_t nbytes)
return (tx_bytes);
}
+static inline int
+vcl_test_write_ds (vcl_test_session_t *ts)
+{
+ vcl_test_stats_t *stats = &ts->stats;
+ int tx_bytes;
+
+ do
+ {
+ stats->tx_xacts++;
+ if (ts->ds[1].len)
+ tx_bytes = vppcom_session_write_segments (ts->fd, ts->ds, 2);
+ else
+ tx_bytes = vppcom_session_write_segments (ts->fd, ts->ds, 1);
+
+ if (tx_bytes < 0)
+ errno = -tx_bytes;
+ if ((tx_bytes == 0) ||
+ ((tx_bytes < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))))
+ stats->rx_eagain++;
+ }
+ while ((tx_bytes == 0) ||
+ ((tx_bytes < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))));
+
+ if (tx_bytes < 0)
+ {
+ vterr ("vppcom_session_write_segments()", -errno);
+ }
+ else
+ stats->tx_bytes += tx_bytes;
+
+ return (tx_bytes);
+}
+
static inline void
dump_help (void)
{
diff --git a/src/plugins/hs_apps/vcl/vcl_test_client.c b/src/plugins/hs_apps/vcl/vcl_test_client.c
index a4a10b562ff..8bac1f00b9d 100644
--- a/src/plugins/hs_apps/vcl/vcl_test_client.c
+++ b/src/plugins/hs_apps/vcl/vcl_test_client.c
@@ -419,13 +419,8 @@ vtc_worker_run_select (vcl_test_client_worker_t *wrk)
if (vcm->incremental_stats)
vtc_inc_stats_check (ts);
}
- if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes) ||
- (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
- {
- clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
- ts->is_done = 1;
- n_active_sessions--;
- }
+ if (vtc_session_check_is_done (ts, check_rx))
+ n_active_sessions -= 1;
}
}
diff --git a/src/plugins/hs_apps/vcl/vcl_test_protos.c b/src/plugins/hs_apps/vcl/vcl_test_protos.c
index cd1ac2b24f4..9c81c5f17a1 100644
--- a/src/plugins/hs_apps/vcl/vcl_test_protos.c
+++ b/src/plugins/hs_apps/vcl/vcl_test_protos.c
@@ -14,6 +14,23 @@
*/
#include <hs_apps/vcl/vcl_test.h>
+#include <http/http.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+
+typedef enum vcl_test_http_state_
+{
+ VCL_TEST_HTTP_IDLE = 0,
+ VCL_TEST_HTTP_IN_PROGRESS,
+ VCL_TEST_HTTP_COMPLETED,
+} vcl_test_http_state_t;
+
+typedef struct vcl_test_http_ctx_t
+{
+ u8 is_server;
+ vcl_test_http_state_t test_state;
+ u64 rem_data;
+} vcl_test_http_ctx_t;
static int
vt_tcp_connect (vcl_test_session_t *ts, vppcom_endpt_t *endpt)
@@ -978,6 +995,418 @@ static const vcl_test_proto_vft_t vcl_test_srtp = {
VCL_TEST_REGISTER_PROTO (VPPCOM_PROTO_SRTP, vcl_test_srtp);
+static void
+vt_http_session_init (vcl_test_session_t *ts, u8 is_server)
+{
+ vcl_test_http_ctx_t *http_ctx;
+
+ http_ctx = malloc (sizeof (vcl_test_http_ctx_t));
+ memset (http_ctx, 0, sizeof (*http_ctx));
+ http_ctx->is_server = is_server;
+ ts->opaque = http_ctx;
+}
+
+static inline void
+vt_http_send_reply_msg (vcl_test_session_t *ts, http_status_code_t status)
+{
+ http_msg_t msg;
+ int rv = 0;
+
+ memset (&msg, 0, sizeof (http_msg_t));
+ msg.type = HTTP_MSG_REPLY;
+ msg.code = status;
+
+ vppcom_data_segment_t segs[1] = { { (u8 *) &msg, sizeof (msg) } };
+
+ do
+ {
+ rv = vppcom_session_write_segments (ts->fd, segs, 1);
+
+ if (rv < 0)
+ {
+ errno = -rv;
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+
+ vterr ("vppcom_session_write()", -errno);
+ break;
+ }
+ }
+ while (rv <= 0);
+}
+
+static inline int
+vt_process_http_server_read_msg (vcl_test_session_t *ts, void *buf,
+ uint32_t nbytes)
+{
+ http_msg_t msg;
+ u8 *target_path = 0;
+ vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque;
+ vcl_test_stats_t *stats = &ts->stats;
+ int rv = 0;
+
+ do
+ {
+ stats->rx_xacts++;
+ rv = vppcom_session_read (ts->fd, buf, nbytes);
+
+ if (rv <= 0)
+ {
+ errno = -rv;
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ {
+ stats->rx_eagain++;
+ continue;
+ }
+
+ vterr ("vppcom_session_read()", -errno);
+ return 0;
+ }
+
+ if (PREDICT_TRUE (vcl_test_http_ctx->test_state ==
+ VCL_TEST_HTTP_IN_PROGRESS))
+ {
+ vcl_test_http_ctx->rem_data -= rv;
+
+ if (vcl_test_http_ctx->rem_data == 0)
+ {
+ vcl_test_http_ctx->test_state = VCL_TEST_HTTP_COMPLETED;
+ vt_http_send_reply_msg (ts, HTTP_STATUS_OK);
+ }
+ }
+ else if (PREDICT_FALSE (vcl_test_http_ctx->test_state ==
+ VCL_TEST_HTTP_IDLE))
+ {
+ msg = *(http_msg_t *) buf;
+
+ /* verify that we have received http post request from client */
+ if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_POST)
+ {
+ vt_http_send_reply_msg (ts, HTTP_STATUS_METHOD_NOT_ALLOWED);
+ vterr ("error! only POST requests allowed from client", 0);
+ return 0;
+ }
+
+ if (msg.data.target_form != HTTP_TARGET_ORIGIN_FORM)
+ {
+ vt_http_send_reply_msg (ts, HTTP_STATUS_BAD_REQUEST);
+ vterr ("error! http target not in origin form", 0);
+ return 0;
+ }
+
+ /* validate target path syntax */
+ if (msg.data.target_path_len)
+ {
+ vec_validate (target_path, msg.data.target_path_len - 1);
+ memcpy (target_path,
+ buf + sizeof (msg) + msg.data.target_path_offset - 1,
+ msg.data.target_path_len + 1);
+ if (http_validate_abs_path_syntax (target_path, 0))
+ {
+ vt_http_send_reply_msg (ts, HTTP_STATUS_BAD_REQUEST);
+ vterr ("error! target path is not absolute", 0);
+ vec_free (target_path);
+ return 0;
+ }
+ vec_free (target_path);
+ }
+
+ /* read body */
+ if (msg.data.body_len)
+ {
+ vcl_test_http_ctx->rem_data = msg.data.body_len;
+ /* | <http_msg_t> | <target> | <headers> | <body> | */
+ vcl_test_http_ctx->rem_data -=
+ (rv - sizeof (msg) - msg.data.body_offset);
+ vcl_test_http_ctx->test_state = VCL_TEST_HTTP_IN_PROGRESS;
+ }
+ }
+
+ if (rv < nbytes)
+ stats->rx_incomp++;
+ }
+ while (rv <= 0);
+
+ stats->rx_bytes += rv;
+ return (rv);
+}
+
+static inline int
+vt_process_http_client_read_msg (vcl_test_session_t *ts, void *buf,
+ uint32_t nbytes)
+{
+ http_msg_t msg;
+ int rv = 0;
+
+ do
+ {
+ rv = vppcom_session_read (ts->fd, buf, nbytes);
+
+ if (rv < 0)
+ {
+ errno = -rv;
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+
+ vterr ("vppcom_session_read()", -errno);
+ break;
+ }
+ }
+ while (!rv);
+
+ msg = *(http_msg_t *) buf;
+
+ if (msg.type == HTTP_MSG_REPLY && msg.code == HTTP_STATUS_OK)
+ vtinf ("received 200 OK from server");
+ else
+ vterr ("received unexpected reply from server", 0);
+
+ return (rv);
+}
+
+static inline int
+vt_process_http_client_write_msg (vcl_test_session_t *ts, void *buf,
+ uint32_t nbytes)
+{
+ http_msg_t msg;
+ http_header_t *req_headers = 0;
+ u8 *headers_buf = 0;
+ u8 *target;
+ vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque;
+ vcl_test_stats_t *stats = &ts->stats;
+ int rv = 0;
+
+ if (PREDICT_TRUE (vcl_test_http_ctx->test_state ==
+ VCL_TEST_HTTP_IN_PROGRESS))
+ {
+ do
+ {
+ rv = vppcom_session_write (
+ ts->fd, buf, clib_min (nbytes, vcl_test_http_ctx->rem_data));
+
+ if (rv <= 0)
+ {
+ errno = -rv;
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ {
+ stats->tx_eagain++;
+ continue;
+ }
+
+ vterr ("vppcom_session_write()", -errno);
+ return 0;
+ }
+
+ vcl_test_http_ctx->rem_data -= rv;
+
+ if (vcl_test_http_ctx->rem_data == 0)
+ {
+ vcl_test_http_ctx->test_state = VCL_TEST_HTTP_COMPLETED;
+ vtinf ("client finished sending %ld bytes of data",
+ ts->cfg.total_bytes);
+ }
+
+ if (rv < nbytes)
+ stats->tx_incomp++;
+ }
+ while (rv <= 0);
+ }
+
+ else if (PREDICT_FALSE (vcl_test_http_ctx->test_state == VCL_TEST_HTTP_IDLE))
+ {
+ http_add_header (
+ &req_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (HTTP_CONTENT_APP_OCTET_STREAM));
+ headers_buf = http_serialize_headers (req_headers);
+ vec_free (req_headers);
+
+ memset (&msg, 0, sizeof (http_msg_t));
+ msg.type = HTTP_MSG_REQUEST;
+ msg.method_type = HTTP_REQ_POST;
+
+ /* target */
+ msg.data.target_form = HTTP_TARGET_ORIGIN_FORM;
+ target = (u8 *) "/vcl_test_http\0";
+ msg.data.target_path_len = strlen ((char *) target);
+
+ /* headers */
+ msg.data.headers_offset = msg.data.target_path_len;
+ msg.data.headers_len = vec_len (headers_buf);
+
+ /* body */
+ msg.data.body_offset = msg.data.headers_offset + msg.data.headers_len;
+ msg.data.body_len = ts->cfg.total_bytes;
+
+ msg.data.len =
+ msg.data.target_path_len + msg.data.headers_len + msg.data.body_len;
+ msg.data.type = HTTP_MSG_DATA_INLINE;
+
+ vppcom_data_segment_t segs[3] = { { (u8 *) &msg, sizeof (msg) },
+ { target, strlen ((char *) target) },
+ { headers_buf,
+ vec_len (headers_buf) } };
+
+ do
+ {
+ rv = vppcom_session_write_segments (ts->fd, segs, 3);
+
+ if (rv <= 0)
+ {
+ errno = -rv;
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ {
+ stats->tx_eagain++;
+ continue;
+ }
+
+ vterr ("vppcom_session_write_segments()", -errno);
+ vec_free (headers_buf);
+ return 0;
+ }
+ }
+ while (rv <= 0);
+
+ vcl_test_http_ctx->test_state = VCL_TEST_HTTP_IN_PROGRESS;
+ vcl_test_http_ctx->rem_data = ts->cfg.total_bytes;
+ vec_free (headers_buf);
+ }
+
+ stats->tx_bytes += rv;
+ return (rv);
+}
+
+static inline int
+vt_process_http_server_write_msg (vcl_test_session_t *ts, void *buf,
+ uint32_t nbytes)
+{
+ return 0;
+}
+
+static inline int
+vt_http_read (vcl_test_session_t *ts, void *buf, uint32_t nbytes)
+{
+ vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque;
+
+ if (vcl_test_http_ctx->is_server)
+ return vt_process_http_server_read_msg (ts, buf, nbytes);
+ else
+ return vt_process_http_client_read_msg (ts, buf, nbytes);
+}
+
+static inline int
+vt_http_write (vcl_test_session_t *ts, void *buf, uint32_t nbytes)
+{
+ vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque;
+
+ if (vcl_test_http_ctx->is_server)
+ return vt_process_http_server_write_msg (ts, buf, nbytes);
+ else
+ return vt_process_http_client_write_msg (ts, buf, nbytes);
+}
+
+static int
+vt_http_connect (vcl_test_session_t *ts, vppcom_endpt_t *endpt)
+{
+ uint32_t flags, flen;
+ int rv;
+
+ ts->fd = vppcom_session_create (VPPCOM_PROTO_HTTP, ts->noblk_connect);
+ if (ts->fd < 0)
+ {
+ vterr ("vppcom_session_create()", ts->fd);
+ return ts->fd;
+ }
+
+ rv = vppcom_session_connect (ts->fd, endpt);
+ if (rv < 0 && rv != VPPCOM_EINPROGRESS)
+ {
+ vterr ("vppcom_session_connect()", rv);
+ return rv;
+ }
+
+ ts->read = vt_http_read;
+ ts->write = vt_http_write;
+
+ if (!ts->noblk_connect)
+ {
+ flags = O_NONBLOCK;
+ flen = sizeof (flags);
+ vppcom_session_attr (ts->fd, VPPCOM_ATTR_SET_FLAGS, &flags, &flen);
+ vtinf ("Test session %d (fd %d) connected.", ts->session_index, ts->fd);
+ }
+
+ vt_http_session_init (ts, 0 /* is_server */);
+
+ return 0;
+}
+
+static int
+vt_http_listen (vcl_test_session_t *ts, vppcom_endpt_t *endpt)
+{
+ int rv;
+
+ ts->fd = vppcom_session_create (VPPCOM_PROTO_HTTP, 1 /* is_nonblocking */);
+ if (ts->fd < 0)
+ {
+ vterr ("vppcom_session_create()", ts->fd);
+ return ts->fd;
+ }
+
+ rv = vppcom_session_bind (ts->fd, endpt);
+ if (rv < 0)
+ {
+ vterr ("vppcom_session_bind()", rv);
+ return rv;
+ }
+
+ rv = vppcom_session_listen (ts->fd, 10);
+ if (rv < 0)
+ {
+ vterr ("vppcom_session_listen()", rv);
+ return rv;
+ }
+
+ return 0;
+}
+
+static int
+vt_http_accept (int listen_fd, vcl_test_session_t *ts)
+{
+ int client_fd;
+
+ client_fd = vppcom_session_accept (listen_fd, &ts->endpt, 0);
+ if (client_fd < 0)
+ {
+ vterr ("vppcom_session_accept()", client_fd);
+ return client_fd;
+ }
+
+ ts->fd = client_fd;
+ ts->is_open = 1;
+ ts->read = vt_http_read;
+ ts->write = vt_http_write;
+
+ vt_http_session_init (ts, 1 /* is_server */);
+
+ return 0;
+}
+
+static int
+vt_http_close (vcl_test_session_t *ts)
+{
+ free (ts->opaque);
+ return 0;
+}
+
+static const vcl_test_proto_vft_t vcl_test_http = {
+ .open = vt_http_connect,
+ .listen = vt_http_listen,
+ .accept = vt_http_accept,
+ .close = vt_http_close,
+};
+
+VCL_TEST_REGISTER_PROTO (VPPCOM_PROTO_HTTP, vcl_test_http);
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/plugins/hs_apps/vcl/vcl_test_server.c b/src/plugins/hs_apps/vcl/vcl_test_server.c
index d17a2089ba7..5de53173784 100644
--- a/src/plugins/hs_apps/vcl/vcl_test_server.c
+++ b/src/plugins/hs_apps/vcl/vcl_test_server.c
@@ -282,11 +282,7 @@ vts_server_process_rx (vcl_test_session_t *conn, int rx_bytes)
if (conn->cfg.test == HS_TEST_TYPE_BI)
{
if (vsm->use_ds)
- {
- (void) vcl_test_write (conn, conn->ds[0].data, conn->ds[0].len);
- if (conn->ds[1].len)
- (void) vcl_test_write (conn, conn->ds[1].data, conn->ds[1].len);
- }
+ (void) vcl_test_write_ds (conn);
else
(void) vcl_test_write (conn, conn->rxbuf, rx_bytes);
}
diff --git a/src/plugins/http/CMakeLists.txt b/src/plugins/http/CMakeLists.txt
index d9cd84a3955..c51a7dce36d 100644
--- a/src/plugins/http/CMakeLists.txt
+++ b/src/plugins/http/CMakeLists.txt
@@ -16,4 +16,5 @@ add_vpp_plugin(http
http.c
http_buffer.c
http_timer.c
+ http_test.c
)
diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c
index 368bd92c525..4f741c2e6b4 100644
--- a/src/plugins/http/http.c
+++ b/src/plugins/http/http.c
@@ -16,11 +16,11 @@
#include <http/http.h>
#include <vnet/session/session.h>
#include <http/http_timer.h>
+#include <http/http_status_codes.h>
static http_main_t http_main;
#define HTTP_FIFO_THRESH (16 << 10)
-#define CONTENT_LEN_STR "Content-Length: "
/* HTTP state machine result */
typedef enum http_sm_result_t_
@@ -30,24 +30,12 @@ typedef enum http_sm_result_t_
HTTP_SM_ERROR = -1,
} http_sm_result_t;
-const char *http_status_code_str[] = {
-#define _(c, s, str) str,
- foreach_http_status_code
-#undef _
-};
-
-const char *http_content_type_str[] = {
-#define _(s, ext, str) str,
- foreach_http_content_type
-#undef _
-};
-
const http_buffer_type_t msg_to_buf_type[] = {
[HTTP_MSG_DATA_INLINE] = HTTP_BUFFER_FIFO,
[HTTP_MSG_DATA_PTR] = HTTP_BUFFER_PTR,
};
-u8 *
+static u8 *
format_http_state (u8 *s, va_list *va)
{
http_state_t state = va_arg (*va, http_state_t);
@@ -83,6 +71,24 @@ format_http_state (u8 *s, va_list *va)
} \
while (0)
+static inline int
+http_state_is_tx_valid (http_conn_t *hc)
+{
+ http_state_t state = hc->http_state;
+ return (state == HTTP_STATE_APP_IO_MORE_DATA ||
+ state == HTTP_STATE_WAIT_APP_REPLY ||
+ state == HTTP_STATE_WAIT_APP_METHOD);
+}
+
+static inline int
+http_state_is_rx_valid (http_conn_t *hc)
+{
+ http_state_t state = hc->http_state;
+ return (state == HTTP_STATE_WAIT_SERVER_REPLY ||
+ state == HTTP_STATE_CLIENT_IO_MORE_DATA ||
+ state == HTTP_STATE_WAIT_CLIENT_METHOD);
+}
+
static inline http_worker_t *
http_worker_get (u32 thread_index)
{
@@ -267,17 +273,21 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts,
app_worker_t *app_wrk;
int rv;
+ ho_hc = http_conn_get_w_thread (ho_hc_index, 0);
+ ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING);
+
if (err)
{
- clib_warning ("ERROR: %d", err);
+ clib_warning ("half-open hc index %d, error: %U", ho_hc_index,
+ format_session_error, err);
+ app_wrk = app_worker_get_if_valid (ho_hc->h_pa_wrk_index);
+ if (app_wrk)
+ app_worker_connect_notify (app_wrk, 0, err, ho_hc->h_pa_app_api_ctx);
return 0;
}
new_hc_index = http_conn_alloc_w_thread (ts->thread_index);
hc = http_conn_get_w_thread (new_hc_index, ts->thread_index);
- ho_hc = http_conn_get_w_thread (ho_hc_index, 0);
-
- ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING);
clib_memcpy_fast (hc, ho_hc, sizeof (*hc));
@@ -360,47 +370,54 @@ http_ts_reset_callback (session_t *ts)
*/
static const char *http_error_template = "HTTP/1.1 %s\r\n"
"Date: %U GMT\r\n"
- "Content-Type: text/html\r\n"
"Connection: close\r\n"
- "Pragma: no-cache\r\n"
"Content-Length: 0\r\n\r\n";
-static const char *http_redirect_template = "HTTP/1.1 %s\r\n";
-
/**
* http response boilerplate
*/
static const char *http_response_template = "HTTP/1.1 %s\r\n"
"Date: %U GMT\r\n"
- "Expires: %U GMT\r\n"
- "Server: %s\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: %lu\r\n\r\n";
+ "Server: %v\r\n"
+ "Content-Length: %llu\r\n"
+ "%s";
-static const char *http_request_template = "GET %s HTTP/1.1\r\n"
- "User-Agent: VPP HTTP client\r\n"
- "Accept: */*\r\n";
+/**
+ * http request boilerplate
+ */
+static const char *http_get_request_template = "GET %s HTTP/1.1\r\n"
+ "Host: %v\r\n"
+ "User-Agent: %v\r\n"
+ "%s";
+
+static const char *http_post_request_template = "POST %s HTTP/1.1\r\n"
+ "Host: %v\r\n"
+ "User-Agent: %v\r\n"
+ "Content-Length: %llu\r\n"
+ "%s";
static u32
-http_send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset)
+http_send_data (http_conn_t *hc, u8 *data, u32 length)
{
const u32 max_burst = 64 << 10;
session_t *ts;
u32 to_send;
- int sent;
+ int rv;
ts = session_get_from_handle (hc->h_tc_session_handle);
- to_send = clib_min (length - offset, max_burst);
- sent = svm_fifo_enqueue (ts->tx_fifo, to_send, data + offset);
-
- if (sent <= 0)
- return offset;
+ to_send = clib_min (length, max_burst);
+ rv = svm_fifo_enqueue (ts->tx_fifo, to_send, data);
+ if (rv <= 0)
+ {
+ clib_warning ("svm_fifo_enqueue failed, rv %d", rv);
+ return 0;
+ }
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
- return (offset + sent);
+ return rv;
}
static void
@@ -416,37 +433,70 @@ http_send_error (http_conn_t *hc, http_status_code_t ec)
now = clib_timebase_now (&hm->timebase);
data = format (0, http_error_template, http_status_code_str[ec],
format_clib_timebase_time, now);
- http_send_data (hc, data, vec_len (data), 0);
+ HTTP_DBG (1, "%v", data);
+ http_send_data (hc, data, vec_len (data));
vec_free (data);
}
static int
http_read_message (http_conn_t *hc)
{
- u32 max_deq, cursize;
+ u32 max_deq;
session_t *ts;
int n_read;
ts = session_get_from_handle (hc->h_tc_session_handle);
- cursize = vec_len (hc->rx_buf);
max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
if (PREDICT_FALSE (max_deq == 0))
return -1;
- vec_validate (hc->rx_buf, cursize + max_deq - 1);
- n_read = svm_fifo_dequeue (ts->rx_fifo, max_deq, hc->rx_buf + cursize);
+ vec_validate (hc->rx_buf, max_deq - 1);
+ n_read = svm_fifo_peek (ts->rx_fifo, 0, max_deq, hc->rx_buf);
ASSERT (n_read == max_deq);
+ HTTP_DBG (1, "read %u bytes from rx_fifo", n_read);
+
+ return 0;
+}
+
+static void
+http_read_message_drop (http_conn_t *hc, u32 len)
+{
+ session_t *ts;
+
+ ts = session_get_from_handle (hc->h_tc_session_handle);
+ svm_fifo_dequeue_drop (ts->rx_fifo, len);
+ vec_reset_length (hc->rx_buf);
if (svm_fifo_is_empty (ts->rx_fifo))
svm_fifo_unset_event (ts->rx_fifo);
+}
- vec_set_len (hc->rx_buf, cursize + n_read);
- return 0;
+static void
+http_read_message_drop_all (http_conn_t *hc)
+{
+ session_t *ts;
+
+ ts = session_get_from_handle (hc->h_tc_session_handle);
+ svm_fifo_dequeue_drop_all (ts->rx_fifo);
+ vec_reset_length (hc->rx_buf);
+
+ if (svm_fifo_is_empty (ts->rx_fifo))
+ svm_fifo_unset_event (ts->rx_fifo);
}
-static int
-v_find_index (u8 *vec, u32 offset, char *str)
+/**
+ * @brief Find the first occurrence of the string in the vector.
+ *
+ * @param vec The vector to be scanned.
+ * @param offset Search offset in the vector.
+ * @param num Maximum number of characters to be searched if non-zero.
+ * @param str The string to be searched.
+ *
+ * @return @c -1 if the string is not found within the vector; index otherwise.
+ */
+static inline int
+v_find_index (u8 *vec, u32 offset, u32 num, char *str)
{
int start_index = offset;
u32 slen = (u32) strnlen_s_inline (str, 16);
@@ -457,7 +507,15 @@ v_find_index (u8 *vec, u32 offset, char *str)
if (vlen <= slen)
return -1;
- for (; start_index < (vlen - slen); start_index++)
+ int end_index = vlen - slen;
+ if (num)
+ {
+ if (num < slen)
+ return -1;
+ end_index = clib_min (end_index, offset + num - slen);
+ }
+
+ for (; start_index <= end_index; start_index++)
{
if (!memcmp (vec + start_index, str, slen))
return start_index;
@@ -466,50 +524,389 @@ v_find_index (u8 *vec, u32 offset, char *str)
return -1;
}
+static void
+http_identify_optional_query (http_conn_t *hc)
+{
+ int i;
+ for (i = hc->target_path_offset;
+ i < (hc->target_path_offset + hc->target_path_len); i++)
+ {
+ if (hc->rx_buf[i] == '?')
+ {
+ hc->target_query_offset = i + 1;
+ hc->target_query_len = hc->target_path_offset + hc->target_path_len -
+ hc->target_query_offset;
+ hc->target_path_len = hc->target_path_len - hc->target_query_len - 1;
+ break;
+ }
+ }
+}
+
static int
-http_parse_header (http_conn_t *hc, int *content_length)
+http_get_target_form (http_conn_t *hc)
{
- unformat_input_t input;
- int i, len;
- u8 *line;
+ int i;
+
+ /* "*" */
+ if ((hc->rx_buf[hc->target_path_offset] == '*') &&
+ (hc->target_path_len == 1))
+ {
+ hc->target_form = HTTP_TARGET_ASTERISK_FORM;
+ return 0;
+ }
+
+ /* 1*( "/" segment ) [ "?" query ] */
+ if (hc->rx_buf[hc->target_path_offset] == '/')
+ {
+ /* drop leading slash */
+ hc->target_path_len--;
+ hc->target_path_offset++;
+ hc->target_form = HTTP_TARGET_ORIGIN_FORM;
+ http_identify_optional_query (hc);
+ return 0;
+ }
- i = v_find_index (hc->rx_buf, hc->rx_buf_offset, CONTENT_LEN_STR);
+ /* scheme "://" host [ ":" port ] *( "/" segment ) [ "?" query ] */
+ i = v_find_index (hc->rx_buf, hc->target_path_offset, hc->target_path_len,
+ "://");
+ if (i > 0)
+ {
+ hc->target_form = HTTP_TARGET_ABSOLUTE_FORM;
+ http_identify_optional_query (hc);
+ return 0;
+ }
+
+ /* host ":" port */
+ for (i = hc->target_path_offset;
+ i < (hc->target_path_offset + hc->target_path_len); i++)
+ {
+ if ((hc->rx_buf[i] == ':') && (isdigit (hc->rx_buf[i + 1])))
+ {
+ hc->target_form = HTTP_TARGET_AUTHORITY_FORM;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int
+http_parse_request_line (http_conn_t *hc, http_status_code_t *ec)
+{
+ int i, target_len;
+ u32 next_line_offset, method_offset;
+
+ /* request-line = method SP request-target SP HTTP-version CRLF */
+ i = v_find_index (hc->rx_buf, 8, 0, "\r\n");
if (i < 0)
{
- clib_warning ("cannot find '%s' in the header!", CONTENT_LEN_STR);
+ clib_warning ("request line incomplete");
+ *ec = HTTP_STATUS_BAD_REQUEST;
return -1;
}
+ HTTP_DBG (0, "request line length: %d", i);
+ hc->control_data_len = i + 2;
+ next_line_offset = hc->control_data_len;
- hc->rx_buf_offset = i;
+ /* there should be at least one more CRLF */
+ if (vec_len (hc->rx_buf) < (next_line_offset + 2))
+ {
+ clib_warning ("malformed message, too short");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+
+ /*
+ * RFC9112 2.2:
+ * In the interest of robustness, a server that is expecting to receive and
+ * parse a request-line SHOULD ignore at least one empty line (CRLF)
+ * received prior to the request-line.
+ */
+ method_offset = hc->rx_buf[0] == '\r' && hc->rx_buf[1] == '\n' ? 2 : 0;
+ /* parse method */
+ if (!memcmp (hc->rx_buf + method_offset, "GET ", 4))
+ {
+ HTTP_DBG (0, "GET method");
+ hc->method = HTTP_REQ_GET;
+ hc->target_path_offset = method_offset + 4;
+ }
+ else if (!memcmp (hc->rx_buf + method_offset, "POST ", 5))
+ {
+ HTTP_DBG (0, "POST method");
+ hc->method = HTTP_REQ_POST;
+ hc->target_path_offset = method_offset + 5;
+ }
+ else
+ {
+ if (hc->rx_buf[method_offset] - 'A' <= 'Z' - hc->rx_buf[method_offset])
+ {
+ clib_warning ("not method name: %8v", hc->rx_buf);
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+ else
+ {
+ clib_warning ("method not implemented: %8v", hc->rx_buf);
+ *ec = HTTP_STATUS_NOT_IMPLEMENTED;
+ return -1;
+ }
+ }
+
+ /* find version */
+ i = v_find_index (hc->rx_buf, next_line_offset - 11, 11, " HTTP/");
+ if (i < 0)
+ {
+ clib_warning ("HTTP version not present");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+ /* verify major version */
+ if (isdigit (hc->rx_buf[i + 6]))
+ {
+ if (hc->rx_buf[i + 6] != '1')
+ {
+ clib_warning ("HTTP major version '%c' not supported",
+ hc->rx_buf[i + 6]);
+ *ec = HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
+ return -1;
+ }
+ }
+ else
+ {
+ clib_warning ("HTTP major version '%c' is not digit", hc->rx_buf[i + 6]);
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+
+ /* parse request-target */
+ HTTP_DBG (0, "http at %d", i);
+ target_len = i - hc->target_path_offset;
+ HTTP_DBG (0, "target_len %d", target_len);
+ if (target_len < 1)
+ {
+ clib_warning ("request-target not present");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+ hc->target_path_len = target_len;
+ hc->target_query_offset = 0;
+ hc->target_query_len = 0;
+ if (http_get_target_form (hc))
+ {
+ clib_warning ("invalid target");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+ HTTP_DBG (0, "request-target path length: %u", hc->target_path_len);
+ HTTP_DBG (0, "request-target path offset: %u", hc->target_path_offset);
+ HTTP_DBG (0, "request-target query length: %u", hc->target_query_len);
+ HTTP_DBG (0, "request-target query offset: %u", hc->target_query_offset);
+
+ /* set buffer offset to nex line start */
+ hc->rx_buf_offset = next_line_offset;
+
+ return 0;
+}
+
+#define expect_char(c) \
+ if (*p++ != c) \
+ { \
+ clib_warning ("unexpected character"); \
+ return -1; \
+ }
+
+#define parse_int(val, mul) \
+ do \
+ { \
+ if (!isdigit (*p)) \
+ { \
+ clib_warning ("expected digit"); \
+ return -1; \
+ } \
+ val += mul * (*p++ - '0'); \
+ } \
+ while (0)
+
+static int
+http_parse_status_line (http_conn_t *hc)
+{
+ int i;
+ u32 next_line_offset;
+ u8 *p, *end;
+ u16 status_code = 0;
- i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "\n");
+ i = v_find_index (hc->rx_buf, 0, 0, "\r\n");
+ /* status-line = HTTP-version SP status-code SP [ reason-phrase ] CRLF */
if (i < 0)
{
- clib_warning ("end of line missing; incomplete data");
+ clib_warning ("status line incomplete");
+ return -1;
+ }
+ HTTP_DBG (0, "status line length: %d", i);
+ if (i < 12)
+ {
+ clib_warning ("status line too short (%d)", i);
+ return -1;
+ }
+ hc->control_data_len = i + 2;
+ next_line_offset = hc->control_data_len;
+ p = hc->rx_buf;
+ end = hc->rx_buf + i;
+
+ /* there should be at least one more CRLF */
+ if (vec_len (hc->rx_buf) < (next_line_offset + 2))
+ {
+ clib_warning ("malformed message, too short");
+ return -1;
+ }
+
+ /* parse version */
+ expect_char ('H');
+ expect_char ('T');
+ expect_char ('T');
+ expect_char ('P');
+ expect_char ('/');
+ expect_char ('1');
+ expect_char ('.');
+ if (!isdigit (*p++))
+ {
+ clib_warning ("invalid HTTP minor version");
+ return -1;
+ }
+
+ /* skip space(s) */
+ if (*p != ' ')
+ {
+ clib_warning ("no space after HTTP version");
+ return -1;
+ }
+ do
+ {
+ p++;
+ if (p == end)
+ {
+ clib_warning ("no status code");
+ return -1;
+ }
+ }
+ while (*p == ' ');
+
+ /* parse status code */
+ if ((end - p) < 3)
+ {
+ clib_warning ("not enough characters for status code");
+ return -1;
+ }
+ parse_int (status_code, 100);
+ parse_int (status_code, 10);
+ parse_int (status_code, 1);
+ if (status_code < 100 || status_code > 599)
+ {
+ clib_warning ("invalid status code %d", status_code);
return -1;
}
+ hc->status_code = status_code;
+ HTTP_DBG (0, "status code: %d", hc->status_code);
+
+ /* set buffer offset to nex line start */
+ hc->rx_buf_offset = next_line_offset;
+ return 0;
+}
+
+static int
+http_identify_headers (http_conn_t *hc, http_status_code_t *ec)
+{
+ int i;
+
+ /* check if we have any header */
+ if ((hc->rx_buf[hc->rx_buf_offset] == '\r') &&
+ (hc->rx_buf[hc->rx_buf_offset + 1] == '\n'))
+ {
+ /* just another CRLF -> no headers */
+ HTTP_DBG (0, "no headers");
+ hc->headers_len = 0;
+ hc->control_data_len += 2;
+ return 0;
+ }
+
+ /* find empty line indicating end of header section */
+ i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, "\r\n\r\n");
+ if (i < 0)
+ {
+ clib_warning ("cannot find header section end");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+ hc->headers_offset = hc->rx_buf_offset;
+ hc->headers_len = i - hc->rx_buf_offset + 2;
+ hc->control_data_len += (hc->headers_len + 2);
+ HTTP_DBG (0, "headers length: %u", hc->headers_len);
+ HTTP_DBG (0, "headers offset: %u", hc->headers_offset);
+
+ return 0;
+}
+
+static int
+http_identify_message_body (http_conn_t *hc, http_status_code_t *ec)
+{
+ unformat_input_t input;
+ int i, len;
+ u8 *line;
+ u64 body_len;
+
+ hc->body_len = 0;
+
+ if (hc->headers_len == 0)
+ {
+ HTTP_DBG (0, "no header, no message-body");
+ return 0;
+ }
+
+ /* TODO check for chunked transfer coding */
+
+ /* try to find Content-Length header */
+ i = v_find_index (hc->rx_buf, hc->headers_offset, hc->headers_len,
+ "Content-Length:");
+ if (i < 0)
+ {
+ HTTP_DBG (0, "Content-Length header not present, no message-body");
+ return 0;
+ }
+ hc->rx_buf_offset = i + 15;
+
+ i = v_find_index (hc->rx_buf, hc->rx_buf_offset, hc->headers_len, "\r\n");
+ if (i < 0)
+ {
+ clib_warning ("end of line missing");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
len = i - hc->rx_buf_offset;
+ if (len < 1)
+ {
+ clib_warning ("invalid header, content length value missing");
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+
line = vec_new (u8, len);
clib_memcpy (line, hc->rx_buf + hc->rx_buf_offset, len);
+ HTTP_DBG (0, "%v", line);
unformat_init_vector (&input, line);
- if (!unformat (&input, CONTENT_LEN_STR "%d", content_length))
+ if (!unformat (&input, "%llu", &body_len))
{
- clib_warning ("failed to unformat content length!");
+ clib_warning ("failed to unformat content length value");
+ *ec = HTTP_STATUS_BAD_REQUEST;
return -1;
}
unformat_free (&input);
+ hc->body_len = body_len;
- /* skip rest of the header */
- hc->rx_buf_offset += len;
- i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "<html>");
- if (i < 0)
- {
- clib_warning ("<html> tag not found");
- return -1;
- }
- hc->rx_buf_offset = i;
+ hc->body_offset = hc->headers_offset + hc->headers_len + 2;
+ HTTP_DBG (0, "body length: %llu", hc->body_len);
+ HTTP_DBG (0, "body offset: %u", hc->body_offset);
return 0;
}
@@ -517,10 +914,13 @@ http_parse_header (http_conn_t *hc, int *content_length)
static http_sm_result_t
http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
{
- int i, rv, content_length;
+ int rv;
http_msg_t msg = {};
app_worker_t *app_wrk;
session_t *as;
+ u32 len, max_enq, body_sent;
+ http_status_code_t ec;
+ http_main_t *hm = &http_main;
rv = http_read_message (hc);
@@ -531,72 +931,74 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
return HTTP_SM_STOP;
}
+ HTTP_DBG (0, "%v", hc->rx_buf);
+
if (vec_len (hc->rx_buf) < 8)
{
clib_warning ("response buffer too short");
goto error;
}
- if ((i = v_find_index (hc->rx_buf, 0, "200 OK")) >= 0)
- {
- msg.type = HTTP_MSG_REPLY;
- msg.content_type = HTTP_CONTENT_TEXT_HTML;
- msg.code = HTTP_STATUS_OK;
- msg.data.type = HTTP_MSG_DATA_INLINE;
- msg.data.len = 0;
+ rv = http_parse_status_line (hc);
+ if (rv)
+ goto error;
- rv = http_parse_header (hc, &content_length);
- if (rv)
- {
- clib_warning ("failed to parse http reply");
- goto error;
- }
- msg.data.len = content_length;
- u32 dlen = vec_len (hc->rx_buf) - hc->rx_buf_offset;
- as = session_get_from_handle (hc->h_pa_session_handle);
- svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
- { &hc->rx_buf[hc->rx_buf_offset], dlen } };
-
- rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2,
- 0 /* allow partial */);
- if (rv < 0)
- {
- clib_warning ("error enqueue");
- return HTTP_SM_ERROR;
- }
+ rv = http_identify_headers (hc, &ec);
+ if (rv)
+ goto error;
- hc->rx_buf_offset += dlen;
- hc->to_recv = content_length - dlen;
+ rv = http_identify_message_body (hc, &ec);
+ if (rv)
+ goto error;
- if (hc->rx_buf_offset == vec_len (hc->rx_buf))
- {
- vec_reset_length (hc->rx_buf);
- hc->rx_buf_offset = 0;
- }
+ /* send at least "control data" which is necessary minimum,
+ * if there is some space send also portion of body */
+ as = session_get_from_handle (hc->h_pa_session_handle);
+ max_enq = svm_fifo_max_enqueue (as->rx_fifo);
+ if (max_enq < hc->control_data_len)
+ {
+ clib_warning ("not enough room for control data in app's rx fifo");
+ goto error;
+ }
+ len = clib_min (max_enq, vec_len (hc->rx_buf));
+
+ msg.type = HTTP_MSG_REPLY;
+ msg.code = hm->sc_by_u16[hc->status_code];
+ msg.data.headers_offset = hc->headers_offset;
+ msg.data.headers_len = hc->headers_len;
+ msg.data.body_offset = hc->body_offset;
+ msg.data.body_len = hc->body_len;
+ msg.data.type = HTTP_MSG_DATA_INLINE;
+ msg.data.len = len;
- if (hc->to_recv == 0)
- {
- hc->rx_buf_offset = 0;
- vec_reset_length (hc->rx_buf);
- http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
- }
- else
- {
- http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
- }
+ svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
+ { hc->rx_buf, len } };
- app_wrk = app_worker_get_if_valid (as->app_wrk_index);
- if (app_wrk)
- app_worker_rx_notify (app_wrk, as);
- return HTTP_SM_STOP;
+ rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */);
+ ASSERT (rv == (sizeof (msg) + len));
+
+ http_read_message_drop (hc, len);
+
+ body_sent = len - hc->control_data_len;
+ hc->to_recv = hc->body_len - body_sent;
+ if (hc->to_recv == 0)
+ {
+ /* all sent, we are done */
+ http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
}
else
{
- clib_warning ("Unknown http method %v", hc->rx_buf);
- goto error;
+ /* stream rest of the response body */
+ http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
}
+ app_wrk = app_worker_get_if_valid (as->app_wrk_index);
+ if (app_wrk)
+ app_worker_rx_notify (app_wrk, as);
+ return HTTP_SM_STOP;
+
error:
+ http_read_message_drop_all (hc);
session_transport_closing_notify (&hc->connection);
session_transport_closed_notify (&hc->connection);
http_disconnect_transport (hc);
@@ -610,9 +1012,9 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
app_worker_t *app_wrk;
http_msg_t msg;
session_t *as;
- int i, rv;
- u32 len;
- u8 *buf;
+ int rv;
+ u32 len, max_enq, body_sent;
+ u64 max_deq;
rv = http_read_message (hc);
@@ -620,64 +1022,76 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
if (rv)
return HTTP_SM_STOP;
+ HTTP_DBG (0, "%v", hc->rx_buf);
+
if (vec_len (hc->rx_buf) < 8)
{
ec = HTTP_STATUS_BAD_REQUEST;
goto error;
}
- if ((i = v_find_index (hc->rx_buf, 0, "GET ")) >= 0)
- {
- hc->method = HTTP_REQ_GET;
- hc->rx_buf_offset = i + 5;
+ rv = http_parse_request_line (hc, &ec);
+ if (rv)
+ goto error;
- i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "HTTP");
- if (i < 0)
- {
- ec = HTTP_STATUS_BAD_REQUEST;
- goto error;
- }
+ rv = http_identify_headers (hc, &ec);
+ if (rv)
+ goto error;
- HTTP_DBG (0, "GET method %v", hc->rx_buf);
- len = i - hc->rx_buf_offset - 1;
- }
- else if ((i = v_find_index (hc->rx_buf, 0, "POST ")) >= 0)
- {
- hc->method = HTTP_REQ_POST;
- hc->rx_buf_offset = i + 6;
- len = vec_len (hc->rx_buf) - hc->rx_buf_offset - 1;
- HTTP_DBG (0, "POST method %v", hc->rx_buf);
- }
- else
+ rv = http_identify_message_body (hc, &ec);
+ if (rv)
+ goto error;
+
+ /* send at least "control data" which is necessary minimum,
+ * if there is some space send also portion of body */
+ as = session_get_from_handle (hc->h_pa_session_handle);
+ max_enq = svm_fifo_max_enqueue (as->rx_fifo);
+ if (max_enq < hc->control_data_len)
{
- HTTP_DBG (0, "Unknown http method %v", hc->rx_buf);
- ec = HTTP_STATUS_METHOD_NOT_ALLOWED;
+ clib_warning ("not enough room for control data in app's rx fifo");
+ ec = HTTP_STATUS_INTERNAL_ERROR;
goto error;
}
-
- buf = &hc->rx_buf[hc->rx_buf_offset];
+ /* do not dequeue more than one HTTP request, we do not support pipelining */
+ max_deq =
+ clib_min (hc->control_data_len + hc->body_len, vec_len (hc->rx_buf));
+ len = clib_min (max_enq, max_deq);
msg.type = HTTP_MSG_REQUEST;
msg.method_type = hc->method;
- msg.content_type = HTTP_CONTENT_TEXT_HTML;
msg.data.type = HTTP_MSG_DATA_INLINE;
msg.data.len = len;
+ msg.data.target_form = hc->target_form;
+ msg.data.target_path_offset = hc->target_path_offset;
+ msg.data.target_path_len = hc->target_path_len;
+ msg.data.target_query_offset = hc->target_query_offset;
+ msg.data.target_query_len = hc->target_query_len;
+ msg.data.headers_offset = hc->headers_offset;
+ msg.data.headers_len = hc->headers_len;
+ msg.data.body_offset = hc->body_offset;
+ msg.data.body_len = hc->body_len;
+
+ svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
+ { hc->rx_buf, len } };
- svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, { buf, len } };
-
- as = session_get_from_handle (hc->h_pa_session_handle);
rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */);
- if (rv < 0 || rv != sizeof (msg) + len)
+ ASSERT (rv == (sizeof (msg) + len));
+
+ body_sent = len - hc->control_data_len;
+ hc->to_recv = hc->body_len - body_sent;
+ if (hc->to_recv == 0)
{
- clib_warning ("failed app enqueue");
- /* This should not happen as we only handle 1 request per session,
- * and fifo is allocated, but going forward we should consider
- * rescheduling */
- return HTTP_SM_ERROR;
+ /* drop everything, we do not support pipelining */
+ http_read_message_drop_all (hc);
+ /* all sent, we are done */
+ http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY);
+ }
+ else
+ {
+ http_read_message_drop (hc, len);
+ /* stream rest of the response body */
+ http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
}
-
- vec_free (hc->rx_buf);
- http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY);
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
if (app_wrk)
@@ -686,7 +1100,7 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
return HTTP_SM_STOP;
error:
-
+ http_read_message_drop_all (hc);
http_send_error (hc, ec);
session_transport_closing_notify (&hc->connection);
http_disconnect_transport (hc);
@@ -698,13 +1112,14 @@ static http_sm_result_t
http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
{
http_main_t *hm = &http_main;
- u8 *header;
- u32 offset;
+ u8 *response;
+ u32 sent;
f64 now;
session_t *as;
http_status_code_t sc;
http_msg_t msg;
int rv;
+ http_sm_result_t sm_result = HTTP_SM_ERROR;
as = session_get_from_handle (hc->h_pa_session_handle);
@@ -725,63 +1140,82 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
goto error;
}
- http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type], as->tx_fifo,
- msg.data.len);
+ if (msg.code >= HTTP_N_STATUS)
+ {
+ clib_warning ("unsupported status code: %d", msg.code);
+ return HTTP_SM_ERROR;
+ }
/*
- * Add headers. For now:
+ * Add "protocol layer" headers:
* - current time
- * - expiration time
* - server name
- * - content type
* - data length
*/
now = clib_timebase_now (&hm->timebase);
-
- switch (msg.code)
- {
- case HTTP_STATUS_OK:
- header =
- format (0, http_response_template, http_status_code_str[msg.code],
- /* Date */
- format_clib_timebase_time, now,
- /* Expires */
- format_clib_timebase_time, now + 600.0,
- /* Server */
- hc->app_name,
- /* Content type */
- http_content_type_str[msg.content_type],
- /* Length */
- msg.data.len);
- break;
- case HTTP_STATUS_MOVED:
- header =
- format (0, http_redirect_template, http_status_code_str[msg.code]);
- /* Location: http(s)://new-place already queued up as data */
- break;
- default:
- return HTTP_SM_ERROR;
+ response = format (0, http_response_template, http_status_code_str[msg.code],
+ /* Date */
+ format_clib_timebase_time, now,
+ /* Server */
+ hc->app_name,
+ /* Length */
+ msg.data.body_len,
+ /* Any headers from app? */
+ msg.data.headers_len ? "" : "\r\n");
+
+ /* Add headers from app (if any) */
+ if (msg.data.headers_len)
+ {
+ HTTP_DBG (0, "got headers from app, len %d", msg.data.headers_len);
+ if (msg.data.type == HTTP_MSG_DATA_PTR)
+ {
+ uword app_headers_ptr;
+ rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr),
+ (u8 *) &app_headers_ptr);
+ ASSERT (rv == sizeof (app_headers_ptr));
+ vec_append (response, uword_to_pointer (app_headers_ptr, u8 *));
+ }
+ else
+ {
+ u32 orig_len = vec_len (response);
+ vec_resize (response, msg.data.headers_len);
+ u8 *p = response + orig_len;
+ rv = svm_fifo_dequeue (as->tx_fifo, msg.data.headers_len, p);
+ ASSERT (rv == msg.data.headers_len);
+ }
}
+ HTTP_DBG (0, "%v", response);
- offset = http_send_data (hc, header, vec_len (header), 0);
- if (offset != vec_len (header))
+ sent = http_send_data (hc, response, vec_len (response));
+ if (sent != vec_len (response))
{
- clib_warning ("couldn't send response header!");
+ clib_warning ("sending status-line and headers failed!");
sc = HTTP_STATUS_INTERNAL_ERROR;
- vec_free (header);
+ vec_free (response);
goto error;
}
- vec_free (header);
+ vec_free (response);
- /* Start sending the actual data */
- http_state_change (hc, HTTP_STATE_APP_IO_MORE_DATA);
+ if (msg.data.body_len)
+ {
+ /* Start sending the actual data */
+ http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type],
+ as->tx_fifo, msg.data.body_len);
+ http_state_change (hc, HTTP_STATE_APP_IO_MORE_DATA);
+ sm_result = HTTP_SM_CONTINUE;
+ }
+ else
+ {
+ /* No response body, we are done */
+ http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
+ sm_result = HTTP_SM_STOP;
+ }
- ASSERT (sp->max_burst_size >= offset);
- sp->max_burst_size -= offset;
- return HTTP_SM_CONTINUE;
+ ASSERT (sp->max_burst_size >= sent);
+ sp->max_burst_size -= sent;
+ return sm_result;
error:
- clib_warning ("unexpected msg type from app %u", msg.type);
http_send_error (hc, sc);
http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
session_transport_closing_notify (&hc->connection);
@@ -794,9 +1228,11 @@ http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
{
http_msg_t msg;
session_t *as;
- u8 *buf = 0, *request;
- u32 offset;
+ u8 *target_buff = 0, *request = 0, *target;
+ u32 sent;
int rv;
+ http_sm_result_t sm_result = HTTP_SM_ERROR;
+ http_state_t next_state;
as = session_get_from_handle (hc->h_pa_session_handle);
@@ -815,31 +1251,131 @@ http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
goto error;
}
- vec_validate (buf, msg.data.len - 1);
- rv = svm_fifo_dequeue (as->tx_fifo, msg.data.len, buf);
- ASSERT (rv == msg.data.len);
+ /* read request target */
+ if (msg.data.type == HTTP_MSG_DATA_PTR)
+ {
+ uword target_ptr;
+ rv = svm_fifo_dequeue (as->tx_fifo, sizeof (target_ptr),
+ (u8 *) &target_ptr);
+ ASSERT (rv == sizeof (target_ptr));
+ target = uword_to_pointer (target_ptr, u8 *);
+ }
+ else
+ {
+ vec_validate (target_buff, msg.data.target_path_len - 1);
+ rv =
+ svm_fifo_dequeue (as->tx_fifo, msg.data.target_path_len, target_buff);
+ ASSERT (rv == msg.data.target_path_len);
+ target = target_buff;
+ }
- request = format (0, http_request_template, buf);
- offset = http_send_data (hc, request, vec_len (request), 0);
- if (offset != vec_len (request))
+ /* currently we support only GET and POST method */
+ if (msg.method_type == HTTP_REQ_GET)
+ {
+ if (msg.data.body_len)
+ {
+ clib_warning ("GET request shouldn't include data");
+ goto error;
+ }
+ /*
+ * Add "protocol layer" headers:
+ * - host
+ * - user agent
+ */
+ request = format (0, http_get_request_template,
+ /* target */
+ target,
+ /* Host */
+ hc->host,
+ /* User-Agent */
+ hc->app_name,
+ /* Any headers from app? */
+ msg.data.headers_len ? "" : "\r\n");
+
+ next_state = HTTP_STATE_WAIT_SERVER_REPLY;
+ sm_result = HTTP_SM_STOP;
+ }
+ else if (msg.method_type == HTTP_REQ_POST)
+ {
+ if (!msg.data.body_len)
+ {
+ clib_warning ("POST request should include data");
+ goto error;
+ }
+ /*
+ * Add "protocol layer" headers:
+ * - host
+ * - user agent
+ * - content length
+ */
+ request = format (0, http_post_request_template,
+ /* target */
+ target,
+ /* Host */
+ hc->host,
+ /* User-Agent */
+ hc->app_name,
+ /* Content-Length */
+ msg.data.body_len,
+ /* Any headers from app? */
+ msg.data.headers_len ? "" : "\r\n");
+
+ http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type],
+ as->tx_fifo, msg.data.body_len);
+
+ next_state = HTTP_STATE_APP_IO_MORE_DATA;
+ sm_result = HTTP_SM_CONTINUE;
+ }
+ else
{
- clib_warning ("sending request failed!");
+ clib_warning ("unsupported method %d", msg.method_type);
goto error;
}
- http_state_change (hc, HTTP_STATE_WAIT_SERVER_REPLY);
+ /* Add headers from app (if any) */
+ if (msg.data.headers_len)
+ {
+ HTTP_DBG (0, "got headers from app, len %d", msg.data.headers_len);
+ if (msg.data.type == HTTP_MSG_DATA_PTR)
+ {
+ uword app_headers_ptr;
+ rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr),
+ (u8 *) &app_headers_ptr);
+ ASSERT (rv == sizeof (app_headers_ptr));
+ vec_append (request, uword_to_pointer (app_headers_ptr, u8 *));
+ }
+ else
+ {
+ u32 orig_len = vec_len (request);
+ vec_resize (request, msg.data.headers_len);
+ u8 *p = request + orig_len;
+ rv = svm_fifo_dequeue (as->tx_fifo, msg.data.headers_len, p);
+ ASSERT (rv == msg.data.headers_len);
+ }
+ }
+ HTTP_DBG (0, "%v", request);
- vec_free (buf);
- vec_free (request);
+ sent = http_send_data (hc, request, vec_len (request));
+ if (sent != vec_len (request))
+ {
+ clib_warning ("sending request-line and headers failed!");
+ sm_result = HTTP_SM_ERROR;
+ goto error;
+ }
- return HTTP_SM_STOP;
+ http_state_change (hc, next_state);
+ goto done;
error:
svm_fifo_dequeue_drop_all (as->tx_fifo);
session_transport_closing_notify (&hc->connection);
session_transport_closed_notify (&hc->connection);
http_disconnect_transport (hc);
- return HTTP_SM_ERROR;
+
+done:
+ vec_free (target_buff);
+ vec_free (request);
+ return sm_result;
}
static http_sm_result_t
@@ -894,14 +1430,14 @@ http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
return HTTP_SM_ERROR;
}
hc->to_recv -= rv;
- HTTP_DBG (1, "drained %d from ts; remains %d", rv, hc->to_recv);
+ HTTP_DBG (1, "drained %d from ts; remains %lu", rv, hc->to_recv);
+ /* Finished transaction:
+ * server back to HTTP_STATE_WAIT_APP_REPLY
+ * client to HTTP_STATE_WAIT_APP_METHOD */
if (hc->to_recv == 0)
- {
- hc->rx_buf_offset = 0;
- vec_reset_length (hc->rx_buf);
- http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
- }
+ http_state_change (hc, hc->is_server ? HTTP_STATE_WAIT_APP_REPLY :
+ HTTP_STATE_WAIT_APP_METHOD);
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
if (app_wrk)
@@ -939,7 +1475,7 @@ http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
if (!http_buffer_is_drained (hb))
{
if (sent && svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
if (svm_fifo_max_enqueue (ts->tx_fifo) < HTTP_FIFO_THRESH)
{
@@ -953,10 +1489,13 @@ http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
else
{
if (sent && svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX_FLUSH);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX_FLUSH);
- /* Finished transaction, back to HTTP_STATE_WAIT_METHOD */
- http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
+ /* Finished transaction:
+ * server back to HTTP_STATE_WAIT_METHOD
+ * client to HTTP_STATE_WAIT_SERVER_REPLY */
+ http_state_change (hc, hc->is_server ? HTTP_STATE_WAIT_CLIENT_METHOD :
+ HTTP_STATE_WAIT_SERVER_REPLY);
http_buffer_free (&hc->tx_buf);
}
@@ -1008,12 +1547,16 @@ http_ts_rx_callback (session_t *ts)
return -1;
}
- if (hc->state == HTTP_CONN_STATE_CLOSED)
+ if (!http_state_is_rx_valid (hc))
{
+ if (hc->state != HTTP_CONN_STATE_CLOSED)
+ clib_warning ("app data req state '%U' session state %u",
+ format_http_state, hc->http_state, hc->state);
svm_fifo_dequeue_drop_all (ts->tx_fifo);
return 0;
}
+ HTTP_DBG (1, "run state machine");
http_req_run_state_machine (hc, 0);
if (hc->state == HTTP_CONN_STATE_TRANSPORT_CLOSED)
@@ -1049,6 +1592,7 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
clib_warning ("no http connection for %u", ts->session_index);
return;
}
+ HTTP_DBG (1, "going to free session %x", ts->opaque);
vec_free (hc->rx_buf);
@@ -1056,6 +1600,12 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
http_conn_timer_stop (hc);
session_transport_delete_notify (&hc->connection);
+
+ if (!hc->is_server)
+ {
+ vec_free (hc->app_name);
+ vec_free (hc->host);
+ }
http_conn_free (hc);
}
@@ -1100,8 +1650,6 @@ http_transport_enable (vlib_main_t *vm, u8 is_en)
return 0;
}
- vec_validate (hm->wrk, vlib_num_workers ());
-
clib_memset (a, 0, sizeof (*a));
clib_memset (options, 0, sizeof (options));
@@ -1123,10 +1671,16 @@ http_transport_enable (vlib_main_t *vm, u8 is_en)
hm->app_index = a->app_index;
vec_free (a->name);
+ if (hm->is_init)
+ return 0;
+
+ vec_validate (hm->wrk, vlib_num_workers ());
+
clib_timebase_init (&hm->timebase, 0 /* GMT */, CLIB_TIMEBASE_DAYLIGHT_NONE,
&vm->clib_time /* share the system clock */);
http_timers_init (vm, http_conn_timeout_cb);
+ hm->is_init = 1;
return 0;
}
@@ -1157,6 +1711,20 @@ http_transport_connect (transport_endpoint_cfg_t *tep)
hc->state = HTTP_CONN_STATE_CONNECTING;
cargs->api_context = hc_index;
+ hc->is_server = 0;
+
+ if (vec_len (app->name))
+ hc->app_name = vec_dup (app->name);
+ else
+ hc->app_name = format (0, "VPP HTTP client");
+
+ if (sep->is_ip4)
+ hc->host = format (0, "%U:%d", format_ip4_address, &sep->ip.ip4,
+ clib_net_to_host_u16 (sep->port));
+ else
+ hc->host = format (0, "%U:%d", format_ip6_address, &sep->ip.ip6,
+ clib_net_to_host_u16 (sep->port));
+
HTTP_DBG (1, "hc ho_index %x", hc_index);
if ((error = vnet_connect (cargs)))
@@ -1209,6 +1777,8 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
lhc->c_s_index = app_listener_index;
lhc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
+ lhc->is_server = 1;
+
if (vec_len (app->name))
lhc->app_name = vec_dup (app->name);
else
@@ -1310,6 +1880,7 @@ http_app_tx_callback (void *session, transport_send_params_t *sp)
max_burst_sz = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
sp->max_burst_size = max_burst_sz;
+ HTTP_DBG (1, "run state machine");
http_req_run_state_machine (hc, sp);
if (hc->state == HTTP_CONN_STATE_APP_CLOSED)
@@ -1455,6 +2026,7 @@ static clib_error_t *
http_transport_init (vlib_main_t *vm)
{
http_main_t *hm = &http_main;
+ int i;
transport_register_protocol (TRANSPORT_PROTO_HTTP, &http_proto,
FIB_PROTOCOL_IP4, ~0);
@@ -1466,7 +2038,26 @@ http_transport_init (vlib_main_t *vm)
hm->first_seg_size = 32 << 20;
hm->fifo_size = 512 << 10;
- return 0;
+ /* Setup u16 to http_status_code_t map */
+ /* Unrecognized status code is equivalent to the x00 status */
+ vec_validate (hm->sc_by_u16, 599);
+ for (i = 100; i < 200; i++)
+ hm->sc_by_u16[i] = HTTP_STATUS_CONTINUE;
+ for (i = 200; i < 300; i++)
+ hm->sc_by_u16[i] = HTTP_STATUS_OK;
+ for (i = 300; i < 400; i++)
+ hm->sc_by_u16[i] = HTTP_STATUS_MULTIPLE_CHOICES;
+ for (i = 400; i < 500; i++)
+ hm->sc_by_u16[i] = HTTP_STATUS_BAD_REQUEST;
+ for (i = 500; i < 600; i++)
+ hm->sc_by_u16[i] = HTTP_STATUS_INTERNAL_ERROR;
+
+ /* Registered status codes */
+#define _(c, s, str) hm->sc_by_u16[c] = HTTP_STATUS_##s;
+ foreach_http_status_code
+#undef _
+
+ return 0;
}
VLIB_INIT_FUNCTION (http_transport_init);
diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h
index c9912dd6db8..5f74edb5e47 100644
--- a/src/plugins/http/http.h
+++ b/src/plugins/http/http.h
@@ -16,6 +16,8 @@
#ifndef SRC_PLUGINS_HTTP_HTTP_H_
#define SRC_PLUGINS_HTTP_HTTP_H_
+#include <ctype.h>
+
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
@@ -49,6 +51,14 @@ typedef struct http_conn_id_
STATIC_ASSERT (sizeof (http_conn_id_t) <= TRANSPORT_CONN_ID_LEN,
"ctx id must be less than TRANSPORT_CONN_ID_LEN");
+typedef struct
+{
+ char *base;
+ uword len;
+} http_token_t;
+
+#define http_token_lit(s) (s), sizeof (s) - 1
+
typedef enum http_conn_state_
{
HTTP_CONN_STATE_LISTEN,
@@ -83,86 +93,96 @@ typedef enum http_msg_type_
HTTP_MSG_REPLY
} http_msg_type_t;
+typedef enum http_target_form_
+{
+ HTTP_TARGET_ORIGIN_FORM,
+ HTTP_TARGET_ABSOLUTE_FORM,
+ HTTP_TARGET_AUTHORITY_FORM,
+ HTTP_TARGET_ASTERISK_FORM
+} http_target_form_t;
+
#define foreach_http_content_type \
- _ (APP_7Z, ".7z", "application / x - 7z - compressed") \
- _ (APP_DOC, ".doc", "application / msword") \
+ _ (APP_7Z, ".7z", "application/x-7z-compressed") \
+ _ (APP_DOC, ".doc", "application/msword") \
_ (APP_DOCX, ".docx", \
- "application / vnd.openxmlformats - " \
+ "application/vnd.openxmlformats-" \
"officedocument.wordprocessingml.document") \
- _ (APP_EPUB, ".epub", "application / epub + zip") \
- _ (APP_FONT, ".eot", "application / vnd.ms - fontobject") \
- _ (APP_JAR, ".jar", "application / java - archive") \
- _ (APP_JSON, ".json", "application / json") \
- _ (APP_JSON_LD, ".jsonld", "application / ld + json") \
- _ (APP_MPKG, ".mpkg", "application / vnd.apple.installer + xml") \
- _ (APP_ODP, ".odp", "application / vnd.oasis.opendocument.presentation") \
- _ (APP_ODS, ".ods", "application / vnd.oasis.opendocument.spreadsheet") \
- _ (APP_ODT, ".odt", "application / vnd.oasis.opendocument.text") \
- _ (APP_OGX, ".ogx", "application / ogg") \
- _ (APP_PDF, ".pdf", "application / pdf") \
- _ (APP_PHP, ".php", "application / x - httpd - php") \
- _ (APP_PPT, ".ppt", "application / vnd.ms - powerpoint") \
- _ (APP_PPTX, ".pptx", "application / vnd.ms - powerpoint") \
- _ (APP_RAR, ".rar", "application / vnd.rar") \
- _ (APP_RTF, ".rtf", "application / rtf") \
- _ (APP_SH, ".sh", "application / x - sh") \
- _ (APP_TAR, ".tar", "application / x - tar") \
- _ (APP_VSD, ".vsd", "application / vnd.visio") \
- _ (APP_XHTML, ".xhtml", "application / xhtml + xml") \
- _ (APP_XLS, ".xls", "application / vnd.ms - excel") \
- _ (APP_XML, ".xml", "application / xml") \
+ _ (APP_EPUB, ".epub", "application/epub+zip") \
+ _ (APP_FONT, ".eot", "application/vnd.ms-fontobject") \
+ _ (APP_JAR, ".jar", "application/java-archive") \
+ _ (APP_JSON, ".json", "application/json") \
+ _ (APP_JSON_LD, ".jsonld", "application/ld+json") \
+ _ (APP_MPKG, ".mpkg", "application/vnd.apple.installer+xml") \
+ _ (APP_ODP, ".odp", "application/vnd.oasis.opendocument.presentation") \
+ _ (APP_ODS, ".ods", "application/vnd.oasis.opendocument.spreadsheet") \
+ _ (APP_ODT, ".odt", "application/vnd.oasis.opendocument.text") \
+ _ (APP_OGX, ".ogx", "application/ogg") \
+ _ (APP_PDF, ".pdf", "application/pdf") \
+ _ (APP_PHP, ".php", "application/x-httpd-php") \
+ _ (APP_PPT, ".ppt", "application/vnd.ms-powerpoint") \
+ _ (APP_PPTX, ".pptx", "application/vnd.ms-powerpoint") \
+ _ (APP_RAR, ".rar", "application/vnd.rar") \
+ _ (APP_RTF, ".rtf", "application/rtf") \
+ _ (APP_SH, ".sh", "application/x-sh") \
+ _ (APP_TAR, ".tar", "application/x-tar") \
+ _ (APP_VSD, ".vsd", "application/vnd.visio") \
+ _ (APP_XHTML, ".xhtml", "application/xhtml+xml") \
+ _ (APP_XLS, ".xls", "application/vnd.ms-excel") \
+ _ (APP_XML, ".xml", "application/xml") \
_ (APP_XSLX, ".xlsx", \
- "application / vnd.openxmlformats - officedocument.spreadsheetml.sheet") \
- _ (APP_XUL, ".xul", "application / vnd.mozilla.xul + xml") \
- _ (APP_ZIP, ".zip", "application / zip") \
- _ (AUDIO_AAC, ".aac", "audio / aac") \
- _ (AUDIO_CD, ".cda", "application / x - cdf") \
- _ (AUDIO_WAV, ".wav", "audio / wav") \
- _ (AUDIO_WEBA, ".weba", "audio / webm") \
- _ (AUDO_MIDI, ".midi", "audio / midi") \
- _ (AUDO_MID, ".mid", "audo / midi") \
- _ (AUDO_MP3, ".mp3", "audio / mpeg") \
- _ (AUDO_OGA, ".oga", "audio / ogg") \
- _ (AUDO_OPUS, ".opus", "audio / opus") \
- _ (APP_OCTET_STREAM, ".bin", "application / octet - stream") \
- _ (BZIP2, ".bz2", "application / x - bzip2") \
- _ (BZIP, ".bz", "application / x - bzip") \
- _ (FONT_OTF, ".otf", "font / otf") \
- _ (FONT_TTF, ".ttf", "font / ttf") \
- _ (FONT_WOFF2, ".woff2", "font / woff2") \
- _ (FONT_WOFF, ".woff", "font / woff") \
- _ (GZIP, ".gz", "application / gzip") \
- _ (IMAGE_AVIF, ".avif", "image / avif") \
- _ (IMAGE_BMP, ".bmp", "image / bmp") \
- _ (IMAGE_GIF, ".gif", "image / gif") \
- _ (IMAGE_ICON, ".ico", "image / vnd.microsoft.icon") \
- _ (IMAGE_JPEG, ".jpeg", "image / jpeg") \
- _ (IMAGE_JPG, ".jpg", "image / jpeg") \
- _ (IMAGE_PNG, ".png", "image / png") \
- _ (IMAGE_SVG, ".svg", "image / svg + xml") \
- _ (IMAGE_TIFF, ".tiff", "image / tiff") \
- _ (IMAGE_TIF, ".tif", "image / tiff") \
- _ (IMAGE_WEBP, ".webp", "image / webp") \
- _ (SCRIPT_CSH, ".csh", "application / x - csh") \
- _ (TEXT_ABIWORD, ".abw", "application / x - abiword") \
- _ (TEXT_ARCHIVE, ".arc", "application / x - freearc") \
- _ (TEXT_AZW, ".azw", "application / vnd.amazon.ebook") \
- _ (TEXT_CALENDAR, ".ics", "text / calendar") \
- _ (TEXT_CSS, ".css", "text / css") \
- _ (TEXT_CSV, ".csv", "text / csv") \
- _ (TEXT_HTM, ".htm", "text / html") \
- _ (TEXT_HTML, ".html", "text / html") \
- _ (TEXT_JS, ".js", "text / javascript") \
- _ (TEXT_MJS, ".mjs", "text / javascript") \
- _ (TEXT_PLAIN, ".txt", "text / plain") \
- _ (VIDEO_3GP2, ".3g2", "video / 3gpp2") \
- _ (VIDEO_3GP, ".3gp", "video / 3gpp") \
- _ (VIDEO_AVI, ".avi", "video / x - msvideo") \
- _ (VIDEO_MP4, ".mp4", "video / mp4") \
- _ (VIDEO_MPEG, ".mpeg", "video / mpeg") \
- _ (VIDEO_OGG, ".ogv", "video / ogg") \
- _ (VIDEO_TS, ".ts", "video / mp2t") \
- _ (VIDEO_WEBM, ".webm", "video / webm")
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") \
+ _ (APP_XUL, ".xul", "application/vnd.mozilla.xul+xml") \
+ _ (APP_X_WWW_FORM_URLENCODED, ".invalid", \
+ "application/x-www-form-urlencoded") \
+ _ (APP_ZIP, ".zip", "application/zip") \
+ _ (AUDIO_AAC, ".aac", "audio/aac") \
+ _ (AUDIO_CD, ".cda", "application/x-cdf") \
+ _ (AUDIO_WAV, ".wav", "audio/wav") \
+ _ (AUDIO_WEBA, ".weba", "audio/webm") \
+ _ (AUDO_MIDI, ".midi", "audio/midi") \
+ _ (AUDO_MID, ".mid", "audo/midi") \
+ _ (AUDO_MP3, ".mp3", "audio/mpeg") \
+ _ (AUDO_OGA, ".oga", "audio/ogg") \
+ _ (AUDO_OPUS, ".opus", "audio/opus") \
+ _ (APP_OCTET_STREAM, ".bin", "application/octet-stream") \
+ _ (BZIP2, ".bz2", "application/x-bzip2") \
+ _ (BZIP, ".bz", "application/x-bzip") \
+ _ (FONT_OTF, ".otf", "font/otf") \
+ _ (FONT_TTF, ".ttf", "font/ttf") \
+ _ (FONT_WOFF2, ".woff2", "font/woff2") \
+ _ (FONT_WOFF, ".woff", "font/woff") \
+ _ (GZIP, ".gz", "application/gzip") \
+ _ (IMAGE_AVIF, ".avif", "image/avif") \
+ _ (IMAGE_BMP, ".bmp", "image/bmp") \
+ _ (IMAGE_GIF, ".gif", "image/gif") \
+ _ (IMAGE_ICON, ".ico", "image/vnd.microsoft.icon") \
+ _ (IMAGE_JPEG, ".jpeg", "image/jpeg") \
+ _ (IMAGE_JPG, ".jpg", "image/jpeg") \
+ _ (IMAGE_PNG, ".png", "image/png") \
+ _ (IMAGE_SVG, ".svg", "image/svg+xml") \
+ _ (IMAGE_TIFF, ".tiff", "image/tiff") \
+ _ (IMAGE_TIF, ".tif", "image/tiff") \
+ _ (IMAGE_WEBP, ".webp", "image/webp") \
+ _ (SCRIPT_CSH, ".csh", "application/x-csh") \
+ _ (TEXT_ABIWORD, ".abw", "application/x-abiword") \
+ _ (TEXT_ARCHIVE, ".arc", "application/x-freearc") \
+ _ (TEXT_AZW, ".azw", "application/vnd.amazon.ebook") \
+ _ (TEXT_CALENDAR, ".ics", "text/calendar") \
+ _ (TEXT_CSS, ".css", "text/css") \
+ _ (TEXT_CSV, ".csv", "text/csv") \
+ _ (TEXT_HTM, ".htm", "text/html") \
+ _ (TEXT_HTML, ".html", "text/html") \
+ _ (TEXT_JS, ".js", "text/javascript") \
+ _ (TEXT_MJS, ".mjs", "text/javascript") \
+ _ (TEXT_PLAIN, ".txt", "text/plain") \
+ _ (VIDEO_3GP2, ".3g2", "video/3gpp2") \
+ _ (VIDEO_3GP, ".3gp", "video/3gpp") \
+ _ (VIDEO_AVI, ".avi", "video/x-msvideo") \
+ _ (VIDEO_MP4, ".mp4", "video/mp4") \
+ _ (VIDEO_MPEG, ".mpeg", "video/mpeg") \
+ _ (VIDEO_OGG, ".ogv", "video/ogg") \
+ _ (VIDEO_TS, ".ts", "video/mp2t") \
+ _ (VIDEO_WEBM, ".webm", "video/webm")
typedef enum http_content_type_
{
@@ -172,12 +192,50 @@ typedef enum http_content_type_
} http_content_type_t;
#define foreach_http_status_code \
+ _ (100, CONTINUE, "100 Continue") \
+ _ (101, SWITCHING_PROTOCOLS, "101 Switching Protocols") \
_ (200, OK, "200 OK") \
+ _ (201, CREATED, "201 Created") \
+ _ (202, ACCEPTED, "202 Accepted") \
+ _ (203, NON_UTHORITATIVE_INFORMATION, "203 Non-Authoritative Information") \
+ _ (204, NO_CONTENT, "204 No Content") \
+ _ (205, RESET_CONTENT, "205 Reset Content") \
+ _ (206, PARTIAL_CONTENT, "206 Partial Content") \
+ _ (300, MULTIPLE_CHOICES, "300 Multiple Choices") \
_ (301, MOVED, "301 Moved Permanently") \
+ _ (302, FOUND, "302 Found") \
+ _ (303, SEE_OTHER, "303 See Other") \
+ _ (304, NOT_MODIFIED, "304 Not Modified") \
+ _ (305, USE_PROXY, "305 Use Proxy") \
+ _ (307, TEMPORARY_REDIRECT, "307 Temporary Redirect") \
+ _ (308, PERMANENT_REDIRECT, "308 Permanent Redirect") \
_ (400, BAD_REQUEST, "400 Bad Request") \
+ _ (401, UNAUTHORIZED, "401 Unauthorized") \
+ _ (402, PAYMENT_REQUIRED, "402 Payment Required") \
+ _ (403, FORBIDDEN, "403 Forbidden") \
_ (404, NOT_FOUND, "404 Not Found") \
_ (405, METHOD_NOT_ALLOWED, "405 Method Not Allowed") \
- _ (500, INTERNAL_ERROR, "500 Internal Server Error")
+ _ (406, NOT_ACCEPTABLE, "406 Not Acceptable") \
+ _ (407, PROXY_AUTHENTICATION_REQUIRED, "407 Proxy Authentication Required") \
+ _ (408, REQUEST_TIMEOUT, "408 Request Timeout") \
+ _ (409, CONFLICT, "409 Conflict") \
+ _ (410, GONE, "410 Gone") \
+ _ (411, LENGTH_REQUIRED, "411 Length Required") \
+ _ (412, PRECONDITION_FAILED, "412 Precondition Failed") \
+ _ (413, CONTENT_TOO_LARGE, "413 Content Too Large") \
+ _ (414, URI_TOO_LONG, "414 URI Too Long") \
+ _ (415, UNSUPPORTED_MEDIA_TYPE, "415 Unsupported Media Type") \
+ _ (416, RANGE_NOT_SATISFIABLE, "416 Range Not Satisfiable") \
+ _ (417, EXPECTATION_FAILED, "417 Expectation Failed") \
+ _ (421, MISDIRECTED_REQUEST, "421 Misdirected Request") \
+ _ (422, UNPROCESSABLE_CONTENT, "422 Unprocessable_Content") \
+ _ (426, UPGRADE_REQUIRED, "426 Upgrade Required") \
+ _ (500, INTERNAL_ERROR, "500 Internal Server Error") \
+ _ (501, NOT_IMPLEMENTED, "501 Not Implemented") \
+ _ (502, BAD_GATEWAY, "502 Bad Gateway") \
+ _ (503, SERVICE_UNAVAILABLE, "503 Service Unavailable") \
+ _ (504, GATEWAY_TIMEOUT, "504 Gateway Timeout") \
+ _ (505, HTTP_VERSION_NOT_SUPPORTED, "505 HTTP Version Not Supported")
typedef enum http_status_code_
{
@@ -187,6 +245,101 @@ typedef enum http_status_code_
HTTP_N_STATUS
} http_status_code_t;
+#define foreach_http_header_name \
+ _ (ACCEPT, "Accept") \
+ _ (ACCEPT_CHARSET, "Accept-Charset") \
+ _ (ACCEPT_ENCODING, "Accept-Encoding") \
+ _ (ACCEPT_LANGUAGE, "Accept-Language") \
+ _ (ACCEPT_RANGES, "Accept-Ranges") \
+ _ (ACCESS_CONTROL_ALLOW_CREDENTIALS, "Access-Control-Allow-Credentials") \
+ _ (ACCESS_CONTROL_ALLOW_HEADERS, "Access-Control-Allow-Headers") \
+ _ (ACCESS_CONTROL_ALLOW_METHODS, "Access-Control-Allow-Methods") \
+ _ (ACCESS_CONTROL_ALLOW_ORIGIN, "Access-Control-Allow-Origin") \
+ _ (ACCESS_CONTROL_EXPOSE_HEADERS, "Access-Control-Expose-Headers") \
+ _ (ACCESS_CONTROL_MAX_AGE, "Access-Control-Max-Age") \
+ _ (ACCESS_CONTROL_REQUEST_HEADERS, "Access-Control-Request-Headers") \
+ _ (ACCESS_CONTROL_REQUEST_METHOD, "Access-Control-Request-Method") \
+ _ (AGE, "Age") \
+ _ (ALLOW, "Allow") \
+ _ (ALPN, "ALPN") \
+ _ (ALT_SVC, "Alt-Svc") \
+ _ (ALT_USED, "Alt-Used") \
+ _ (ALTERNATES, "Alternates") \
+ _ (AUTHENTICATION_CONTROL, "Authentication-Control") \
+ _ (AUTHENTICATION_INFO, "Authentication-Info") \
+ _ (AUTHORIZATION, "Authorization") \
+ _ (CACHE_CONTROL, "Cache-Control") \
+ _ (CACHE_STATUS, "Cache-Status") \
+ _ (CAPSULE_PROTOCOL, "Capsule-Protocol") \
+ _ (CDN_CACHE_CONTROL, "CDN-Cache-Control") \
+ _ (CDN_LOOP, "CDN-Loop") \
+ _ (CLIENT_CERT, "Client-Cert") \
+ _ (CLIENT_CERT_CHAIN, "Client-Cert-Chain") \
+ _ (CLOSE, "Close") \
+ _ (CONNECTION, "Connection") \
+ _ (CONTENT_DIGEST, "Content-Digest") \
+ _ (CONTENT_DISPOSITION, "Content-Disposition") \
+ _ (CONTENT_ENCODING, "Content-Encoding") \
+ _ (CONTENT_LANGUAGE, "Content-Language") \
+ _ (CONTENT_LENGTH, "Content-Length") \
+ _ (CONTENT_LOCATION, "Content-Location") \
+ _ (CONTENT_RANGE, "Content-Range") \
+ _ (CONTENT_TYPE, "Content-Type") \
+ _ (COOKIE, "Cookie") \
+ _ (DATE, "Date") \
+ _ (DIGEST, "Digest") \
+ _ (DPOP, "DPoP") \
+ _ (DPOP_NONCE, "DPoP-Nonce") \
+ _ (EARLY_DATA, "Early-Data") \
+ _ (ETAG, "ETag") \
+ _ (EXPECT, "Expect") \
+ _ (EXPIRES, "Expires") \
+ _ (FORWARDED, "Forwarded") \
+ _ (FROM, "From") \
+ _ (HOST, "Host") \
+ _ (IF_MATCH, "If-Match") \
+ _ (IF_MODIFIED_SINCE, "If-Modified-Since") \
+ _ (IF_NONE_MATCH, "If-None-Match") \
+ _ (IF_RANGE, "If-Range") \
+ _ (IF_UNMODIFIED_SINCE, "If-Unmodified-Since") \
+ _ (KEEP_ALIVE, "Keep-Alive") \
+ _ (LAST_MODIFIED, "Last-Modified") \
+ _ (LINK, "Link") \
+ _ (LOCATION, "Location") \
+ _ (MAX_FORWARDS, "Max-Forwards") \
+ _ (ORIGIN, "Origin") \
+ _ (PRIORITY, "Priority") \
+ _ (PROXY_AUTHENTICATE, "Proxy-Authenticate") \
+ _ (PROXY_AUTHENTICATION_INFO, "Proxy-Authentication-Info") \
+ _ (PROXY_AUTHORIZATION, "Proxy-Authorization") \
+ _ (PROXY_STATUS, "Proxy-Status") \
+ _ (RANGE, "Range") \
+ _ (REFERER, "Referer") \
+ _ (REPR_DIGEST, "Repr-Digest") \
+ _ (SET_COOKIE, "Set-Cookie") \
+ _ (SIGNATURE, "Signature") \
+ _ (SIGNATURE_INPUT, "Signature-Input") \
+ _ (STRICT_TRANSPORT_SECURITY, "Strict-Transport-Security") \
+ _ (RETRY_AFTER, "Retry-After") \
+ _ (SERVER, "Server") \
+ _ (TE, "TE") \
+ _ (TRAILER, "Trailer") \
+ _ (TRANSFER_ENCODING, "Transfer-Encoding") \
+ _ (UPGRADE, "Upgrade") \
+ _ (USER_AGENT, "User-Agent") \
+ _ (VARY, "Vary") \
+ _ (VIA, "Via") \
+ _ (WANT_CONTENT_DIGEST, "Want-Content-Digest") \
+ _ (WANT_REPR_DIGEST, "Want-Repr-Digest") \
+ _ (WWW_AUTHENTICATE, "WWW-Authenticate")
+
+typedef enum http_header_name_
+{
+#define _(sym, str) HTTP_HEADER_##sym,
+ foreach_http_header_name
+#undef _
+} http_header_name_t;
+
typedef enum http_msg_data_type_
{
HTTP_MSG_DATA_INLINE,
@@ -197,6 +350,15 @@ typedef struct http_msg_data_
{
http_msg_data_type_t type;
u64 len;
+ http_target_form_t target_form;
+ u32 target_path_offset;
+ u32 target_path_len;
+ u32 target_query_offset;
+ u32 target_query_len;
+ u32 headers_offset;
+ u32 headers_len;
+ u32 body_offset;
+ u64 body_len;
u8 data[0];
} http_msg_data_t;
@@ -208,7 +370,6 @@ typedef struct http_msg_
http_req_method_t method_type;
http_status_code_t code;
};
- http_content_type_t content_type;
http_msg_data_t data;
} http_msg_t;
@@ -228,6 +389,8 @@ typedef struct http_tc_
http_conn_state_t state;
u32 timer_handle;
u8 *app_name;
+ u8 *host;
+ u8 is_server;
/*
* Current request
@@ -237,8 +400,19 @@ typedef struct http_tc_
u8 *rx_buf;
u32 rx_buf_offset;
http_buffer_t tx_buf;
- u32 to_recv;
+ u64 to_recv;
u32 bytes_dequeued;
+ u32 control_data_len; /* start line + headers + empty line */
+ http_target_form_t target_form;
+ u32 target_path_offset;
+ u32 target_path_len;
+ u32 target_query_offset;
+ u32 target_query_len;
+ u32 headers_offset;
+ u32 headers_len;
+ u32 body_offset;
+ u64 body_len;
+ u16 status_code;
} http_conn_t;
typedef struct http_worker_
@@ -254,10 +428,12 @@ typedef struct http_main_
clib_timebase_t timebase;
+ u16 *sc_by_u16;
/*
* Runtime config
*/
u8 debug_level;
+ u8 is_init;
/*
* Config
@@ -267,14 +443,535 @@ typedef struct http_main_
u32 fifo_size;
} http_main_t;
-static inline int
-http_state_is_tx_valid (http_conn_t *hc)
+always_inline int
+_validate_target_syntax (u8 *target, int is_query, int *is_encoded)
+{
+ int i, encoded = 0;
+
+ static uword valid_chars[4] = {
+ /* !$&'()*+,-./0123456789:;= */
+ 0x2fffffd200000000,
+ /* @ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ */
+ 0x47fffffe87ffffff,
+ 0x0000000000000000,
+ 0x0000000000000000,
+ };
+
+ for (i = 0; i < vec_len (target); i++)
+ {
+ if (clib_bitmap_get_no_check (valid_chars, target[i]))
+ continue;
+ /* target was already split after first question mark,
+ * for query it is valid character */
+ if (is_query && target[i] == '?')
+ continue;
+ /* pct-encoded = "%" HEXDIG HEXDIG */
+ if (target[i] == '%')
+ {
+ if ((i + 2) > vec_len (target))
+ return -1;
+ if (!isxdigit (target[i + 1]) || !isxdigit (target[i + 2]))
+ return -1;
+ i += 2;
+ encoded = 1;
+ continue;
+ }
+ clib_warning ("invalid character %d", target[i]);
+ return -1;
+ }
+ if (is_encoded)
+ *is_encoded = encoded;
+ return 0;
+}
+
+/**
+ * An "absolute-path" rule validation (RFC9110 section 4.1).
+ *
+ * @param path Target path to validate.
+ * @param is_encoded Return flag that indicates if percent-encoded (optional).
+ *
+ * @return @c 0 on success.
+ */
+always_inline int
+http_validate_abs_path_syntax (u8 *path, int *is_encoded)
+{
+ return _validate_target_syntax (path, 0, is_encoded);
+}
+
+/**
+ * A "query" rule validation (RFC3986 section 2.1).
+ *
+ * @param query Target query to validate.
+ * @param is_encoded Return flag that indicates if percent-encoded (optional).
+ *
+ * @return @c 0 on success.
+ */
+always_inline int
+http_validate_query_syntax (u8 *query, int *is_encoded)
+{
+ return _validate_target_syntax (query, 1, is_encoded);
+}
+
+#define htoi(x) (isdigit (x) ? (x - '0') : (tolower (x) - 'a' + 10))
+
+/**
+ * Decode percent-encoded data.
+ *
+ * @param src Data to decode.
+ *
+ * @return New vector with decoded data.
+ *
+ * The caller is always responsible to free the returned vector.
+ */
+always_inline u8 *
+http_percent_decode (u8 *src)
+{
+ int i;
+ u8 *decoded_uri = 0;
+
+ for (i = 0; i < vec_len (src); i++)
+ {
+ if (src[i] == '%')
+ {
+ u8 c = (htoi (src[i + 1]) << 4) | htoi (src[i + 2]);
+ vec_add1 (decoded_uri, c);
+ i += 2;
+ }
+ else
+ vec_add1 (decoded_uri, src[i]);
+ }
+ return decoded_uri;
+}
+
+/**
+ * Remove dot segments from path (RFC3986 section 5.2.4)
+ *
+ * @param path Path to sanitize.
+ *
+ * @return New vector with sanitized path.
+ *
+ * The caller is always responsible to free the returned vector.
+ */
+always_inline u8 *
+http_path_remove_dot_segments (u8 *path)
+{
+ u32 *segments = 0, *segments_len = 0, segment_len;
+ u8 *new_path = 0;
+ int i, ii;
+
+ if (!path)
+ return vec_new (u8, 0);
+
+ segments = vec_new (u32, 1);
+ /* first segment */
+ segments[0] = 0;
+ /* find all segments */
+ for (i = 1; i < (vec_len (path) - 1); i++)
+ {
+ if (path[i] == '/')
+ vec_add1 (segments, i + 1);
+ }
+ /* dummy tail */
+ vec_add1 (segments, vec_len (path));
+
+ /* scan all segments for "." and ".." */
+ segments_len = vec_new (u32, vec_len (segments) - 1);
+ for (i = 0; i < vec_len (segments_len); i++)
+ {
+ segment_len = segments[i + 1] - segments[i];
+ if (segment_len == 2 && path[segments[i]] == '.')
+ segment_len = 0;
+ else if (segment_len == 3 && path[segments[i]] == '.' &&
+ path[segments[i] + 1] == '.')
+ {
+ segment_len = 0;
+ /* remove parent (if any) */
+ for (ii = i - 1; ii >= 0; ii--)
+ {
+ if (segments_len[ii])
+ {
+ segments_len[ii] = 0;
+ break;
+ }
+ }
+ }
+ segments_len[i] = segment_len;
+ }
+
+ /* we might end with empty path, so return at least empty vector */
+ new_path = vec_new (u8, 0);
+ /* append all valid segments */
+ for (i = 0; i < vec_len (segments_len); i++)
+ {
+ if (segments_len[i])
+ vec_add (new_path, path + segments[i], segments_len[i]);
+ }
+ vec_free (segments);
+ vec_free (segments_len);
+ return new_path;
+}
+
+always_inline int
+_parse_field_name (u8 **pos, u8 *end, u8 **field_name_start,
+ u32 *field_name_len)
+{
+ u32 name_len = 0;
+ u8 *p;
+
+ static uword tchar[4] = {
+ /* !#$%'*+-.0123456789 */
+ 0x03ff6cba00000000,
+ /* ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~ */
+ 0x57ffffffc7fffffe,
+ 0x0000000000000000,
+ 0x0000000000000000,
+ };
+
+ p = *pos;
+
+ *field_name_start = p;
+ while (p != end)
+ {
+ if (clib_bitmap_get_no_check (tchar, *p))
+ {
+ name_len++;
+ p++;
+ }
+ else if (*p == ':')
+ {
+ if (name_len == 0)
+ {
+ clib_warning ("empty field name");
+ return -1;
+ }
+ *field_name_len = name_len;
+ p++;
+ *pos = p;
+ return 0;
+ }
+ else
+ {
+ clib_warning ("invalid character %d", *p);
+ return -1;
+ }
+ }
+ clib_warning ("field name end not found");
+ return -1;
+}
+
+always_inline int
+_parse_field_value (u8 **pos, u8 *end, u8 **field_value_start,
+ u32 *field_value_len)
+{
+ u32 value_len = 0;
+ u8 *p;
+
+ p = *pos;
+
+ /* skip leading whitespace */
+ while (1)
+ {
+ if (p == end)
+ {
+ clib_warning ("field value not found");
+ return -1;
+ }
+ else if (*p != ' ' && *p != '\t')
+ {
+ break;
+ }
+ p++;
+ }
+
+ *field_value_start = p;
+ while (p != end)
+ {
+ if (*p == '\r')
+ {
+ if ((end - p) < 1)
+ {
+ clib_warning ("incorrect field line end");
+ return -1;
+ }
+ p++;
+ if (*p == '\n')
+ {
+ if (value_len == 0)
+ {
+ clib_warning ("empty field value");
+ return -1;
+ }
+ p++;
+ *pos = p;
+ /* skip trailing whitespace */
+ p = *field_value_start + value_len - 1;
+ while (*p == ' ' || *p == '\t')
+ {
+ p--;
+ value_len--;
+ }
+ *field_value_len = value_len;
+ return 0;
+ }
+ clib_warning ("CR without LF");
+ return -1;
+ }
+ if (*p < ' ' && *p != '\t')
+ {
+ clib_warning ("invalid character %d", *p);
+ return -1;
+ }
+ p++;
+ value_len++;
+ }
+
+ clib_warning ("field value end not found");
+ return -1;
+}
+
+typedef struct
+{
+ u8 *name;
+ u8 *value;
+} http_header_ht_t;
+
+typedef struct
+{
+ http_token_t name;
+ http_token_t value;
+} http_header_t;
+
+typedef struct
+{
+ http_header_ht_t *headers;
+ uword *value_by_name;
+} http_header_table_t;
+
+/**
+ * Free header table's memory.
+ *
+ * @param ht Header table to free.
+ */
+always_inline void
+http_free_header_table (http_header_table_t *ht)
+{
+ http_header_ht_t *header;
+ vec_foreach (header, ht->headers)
+ {
+ vec_free (header->name);
+ vec_free (header->value);
+ }
+ vec_free (ht->headers);
+ hash_free (ht->value_by_name);
+ clib_mem_free (ht);
+}
+
+/**
+ * Parse headers in given vector.
+ *
+ * @param headers Vector to parse.
+ * @param [out] header_table Parsed headers in case of success.
+ *
+ * @return @c 0 on success.
+ *
+ * The caller is responsible to free the returned @c header_table
+ * using @c http_free_header_table .
+ */
+always_inline int
+http_parse_headers (u8 *headers, http_header_table_t **header_table)
+{
+ u8 *pos, *end, *name_start, *value_start, *name;
+ u32 name_len, value_len;
+ int rv;
+ http_header_ht_t *header;
+ http_header_table_t *ht;
+ uword *p;
+
+ end = headers + vec_len (headers);
+ pos = headers;
+
+ ht = clib_mem_alloc (sizeof (*ht));
+ ht->value_by_name = hash_create_string (0, sizeof (uword));
+ ht->headers = 0;
+ do
+ {
+ rv = _parse_field_name (&pos, end, &name_start, &name_len);
+ if (rv != 0)
+ {
+ http_free_header_table (ht);
+ return rv;
+ }
+ rv = _parse_field_value (&pos, end, &value_start, &value_len);
+ if (rv != 0)
+ {
+ http_free_header_table (ht);
+ return rv;
+ }
+ name = vec_new (u8, name_len);
+ clib_memcpy (name, name_start, name_len);
+ vec_terminate_c_string (name);
+ /* check if header is repeated */
+ p = hash_get_mem (ht->value_by_name, name);
+ if (p)
+ {
+ /* if yes combine values */
+ header = vec_elt_at_index (ht->headers, p[0]);
+ vec_pop (header->value); /* drop null byte */
+ header->value = format (header->value, ", %U%c", format_ascii_bytes,
+ value_start, value_len, 0);
+ vec_free (name);
+ continue;
+ }
+ /* or create new record */
+ vec_add2 (ht->headers, header, sizeof (*header));
+ header->name = name;
+ header->value = vec_new (u8, value_len);
+ clib_memcpy (header->value, value_start, value_len);
+ vec_terminate_c_string (header->value);
+ hash_set_mem (ht->value_by_name, header->name, header - ht->headers);
+ }
+ while (pos != end);
+
+ *header_table = ht;
+
+ return 0;
+}
+
+/**
+ * Try to find given header name in header table.
+ *
+ * @param header_table Header table to search.
+ * @param name Header name to match.
+ *
+ * @return Header's value in case of success, @c 0 otherwise.
+ */
+always_inline const char *
+http_get_header (http_header_table_t *header_table, const char *name)
{
- http_state_t state = hc->http_state;
- return (state == HTTP_STATE_APP_IO_MORE_DATA ||
- state == HTTP_STATE_CLIENT_IO_MORE_DATA ||
- state == HTTP_STATE_WAIT_APP_REPLY ||
- state == HTTP_STATE_WAIT_APP_METHOD);
+ uword *p;
+ http_header_ht_t *header;
+
+ p = hash_get_mem (header_table->value_by_name, name);
+ if (p)
+ {
+ header = vec_elt_at_index (header_table->headers, p[0]);
+ return (const char *) header->value;
+ }
+
+ return 0;
+}
+
+/**
+ * Add header to the list.
+ *
+ * @param headers Header list.
+ * @param name Pointer to header's name buffer.
+ * @param name_len Length of the name.
+ * @param value Pointer to header's value buffer.
+ * @param value_len Length of the value.
+ *
+ * @note Headers added at protocol layer: Date, Server, Content-Length
+ */
+always_inline void
+http_add_header (http_header_t **headers, const char *name, uword name_len,
+ const char *value, uword value_len)
+{
+ http_header_t *header;
+ vec_add2 (*headers, header, 1);
+ header->name.base = (char *) name;
+ header->name.len = name_len;
+ header->value.base = (char *) value;
+ header->value.len = value_len;
+}
+
+/**
+ * Serialize the header list.
+ *
+ * @param headers Header list to serialize.
+ *
+ * @return New vector with serialized headers.
+ *
+ * The caller is always responsible to free the returned vector.
+ */
+always_inline u8 *
+http_serialize_headers (http_header_t *headers)
+{
+ u8 *headers_buf = 0, *dst;
+ u32 headers_buf_len = 2;
+ http_header_t *header;
+
+ vec_foreach (header, headers)
+ headers_buf_len += header->name.len + header->value.len + 4;
+
+ vec_validate (headers_buf, headers_buf_len - 1);
+ dst = headers_buf;
+
+ vec_foreach (header, headers)
+ {
+ clib_memcpy (dst, header->name.base, header->name.len);
+ dst += header->name.len;
+ *dst++ = ':';
+ *dst++ = ' ';
+ clib_memcpy (dst, header->value.base, header->value.len);
+ dst += header->value.len;
+ *dst++ = '\r';
+ *dst++ = '\n';
+ }
+ *dst++ = '\r';
+ *dst = '\n';
+ return headers_buf;
+}
+
+typedef struct
+{
+ ip46_address_t ip;
+ u16 port;
+ u8 is_ip4;
+} http_uri_t;
+
+always_inline int
+http_parse_authority_form_target (u8 *target, http_uri_t *authority)
+{
+ unformat_input_t input;
+ u32 port;
+ int rv = 0;
+
+ unformat_init_vector (&input, vec_dup (target));
+ if (unformat (&input, "[%U]:%d", unformat_ip6_address, &authority->ip.ip6,
+ &port))
+ {
+ authority->port = clib_host_to_net_u16 (port);
+ authority->is_ip4 = 0;
+ }
+ else if (unformat (&input, "%U:%d", unformat_ip4_address, &authority->ip.ip4,
+ &port))
+ {
+ authority->port = clib_host_to_net_u16 (port);
+ authority->is_ip4 = 1;
+ }
+ /* TODO reg-name resolution */
+ else
+ {
+ clib_warning ("unsupported format '%v'", target);
+ rv = -1;
+ }
+ unformat_free (&input);
+ return rv;
+}
+
+always_inline u8 *
+http_serialize_authority_form_target (http_uri_t *authority)
+{
+ u8 *s;
+
+ if (authority->is_ip4)
+ s = format (0, "%U:%d", format_ip4_address, &authority->ip.ip4,
+ clib_net_to_host_u16 (authority->port));
+ else
+ s = format (0, "[%U]:%d", format_ip6_address, &authority->ip.ip6,
+ clib_net_to_host_u16 (authority->port));
+
+ return s;
}
#endif /* SRC_PLUGINS_HTTP_HTTP_H_ */
diff --git a/src/plugins/http/http_buffer.c b/src/plugins/http/http_buffer.c
index f3dc308dbf8..bc1b8c08630 100644
--- a/src/plugins/http/http_buffer.c
+++ b/src/plugins/http/http_buffer.c
@@ -173,7 +173,7 @@ buf_ptr_drain (http_buffer_t *hb, u32 len)
bf->segs[1].data += len;
bf->segs[0].len -= len;
- HTTP_DBG (1, "drained %u left %u", len, bf->segs[1].len);
+ HTTP_DBG (1, "drained %u left %u", len, bf->segs[0].len);
if (!bf->segs[0].len)
{
diff --git a/src/plugins/http/http_content_types.h b/src/plugins/http/http_content_types.h
new file mode 100644
index 00000000000..ddc02566db7
--- /dev/null
+++ b/src/plugins/http/http_content_types.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_PLUGINS_HTTP_HTTP_CONTENT_TYPES_H_
+#define SRC_PLUGINS_HTTP_HTTP_CONTENT_TYPES_H_
+
+#include <http/http.h>
+
+static http_token_t http_content_types[] = {
+#define _(s, ext, str) { http_token_lit (str) },
+ foreach_http_content_type
+#undef _
+};
+
+#define http_content_type_token(e) \
+ http_content_types[e].base, http_content_types[e].len
+
+#endif /* SRC_PLUGINS_HTTP_HTTP_CONTENT_TYPES_H_ */
diff --git a/src/plugins/http/http_header_names.h b/src/plugins/http/http_header_names.h
new file mode 100644
index 00000000000..99acac786db
--- /dev/null
+++ b/src/plugins/http/http_header_names.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_PLUGINS_HTTP_HTTP_HEADER_NAMES_H_
+#define SRC_PLUGINS_HTTP_HTTP_HEADER_NAMES_H_
+
+#include <http/http.h>
+
+static http_token_t http_header_names[] = {
+#define _(sym, str) { http_token_lit (str) },
+ foreach_http_header_name
+#undef _
+};
+
+#define http_header_name_token(e) \
+ http_header_names[e].base, http_header_names[e].len
+
+#define http_header_name_str(e) http_header_names[e].base
+
+#endif /* SRC_PLUGINS_HTTP_HTTP_HEADER_NAMES_H_ */
diff --git a/src/plugins/http/http_plugin.rst b/src/plugins/http/http_plugin.rst
new file mode 100644
index 00000000000..56da3a810b9
--- /dev/null
+++ b/src/plugins/http/http_plugin.rst
@@ -0,0 +1,507 @@
+.. _http_plugin:
+
+.. toctree::
+
+HTTP Plugin
+===========
+
+Overview
+--------
+
+This plugin adds the HTTP protocol to VPP's Host Stack.
+As a result parsing and serializing of HTTP/1 requests or responses are available for internal VPP applications.
+
+Usage
+-----
+
+The plugin exposes following inline functions: ``http_validate_abs_path_syntax``, ``http_validate_query_syntax``,
+``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_parse_headers``, ``http_get_header``,
+``http_free_header_table``, ``http_add_header``, ``http_serialize_headers``.
+
+It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications.
+
+Server application
+^^^^^^^^^^^^^^^^^^
+
+Server application sets ``TRANSPORT_PROTO_HTTP`` as ``transport_proto`` in session endpoint configuration when registering to listen.
+
+Receiving data
+""""""""""""""
+
+HTTP plugin sends message header with metadata for parsing, in form of offset and length, followed by all data bytes as received from transport.
+
+Application will get pre-parsed following items:
+
+* HTTP method
+* target form
+* target path offset and length
+* target query offset and length
+* header section offset and length
+* body offset and length
+
+The example below reads HTTP message header in ``builtin_app_rx_callback``, which is first step application should do:
+
+.. code-block:: C
+
+ #include <http/http.h>
+ http_msg_t msg;
+ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
+ ASSERT (rv == sizeof (msg));
+
+As next step application might validate message and method type, for example application only expects to receive GET requests:
+
+.. code-block:: C
+
+ if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
+ {
+ /* your error handling */
+ }
+
+Now application can start reading HTTP data. First let's read the target path:
+
+.. code-block:: C
+
+ u8 *target_path;
+ vec_validate (target_path, msg.data.target_path_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, msg.data.target_path_len, target_path);
+ ASSERT (rv == msg.data.target_path_len);
+
+Application might also want to know target form which is stored in ``msg.data.target_form``, you can read more about target forms in RFC9112 section 3.2.
+In case of origin form HTTP plugin always sets ``target_path_offset`` after leading slash character.
+
+Example bellow validates "absolute-path" rule, as described in RFC9110 section 4.1, in case of target in origin form, additionally application can get information if percent encoding is used and decode path:
+
+.. code-block:: C
+
+ int is_encoded = 0;
+ if (msg.data.target_form == HTTP_TARGET_ORIGIN_FORM)
+ {
+ if (http_validate_abs_path_syntax (target_path, &is_encoded))
+ {
+ /* your error handling */
+ }
+ if (is_encoded)
+ {
+ u8 *decoded = http_percent_decode (target_path);
+ vec_free (target_path);
+ target_path = decoded;
+ }
+ }
+
+More on topic when to decode in RFC3986 section 2.4.
+
+When application serves static files, it is highly recommended to sanitize target path by removing dot segments (you don't want to risk path traversal attack):
+
+.. code-block:: C
+
+ u8 *sanitized_path;
+ sanitized_path = http_path_remove_dot_segments (target_path);
+
+Let's move to target query which is optional. Percent encoding might be used too, but we skip it for brevity:
+
+.. code-block:: C
+
+ u8 *target_query = 0;
+ if (msg.data.target_query_len)
+ {
+ vec_validate (target_query, msg.data.target_query_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_query_offset,
+ msg.data.target_query_len, target_query);
+ ASSERT (rv == msg.data.target_query_len);
+ if (http_validate_query_syntax (target_query, 0))
+ {
+ /* your error handling */
+ }
+ }
+
+And now for something completely different, headers.
+Headers are parsed using a generic algorithm, independent of the individual header names.
+When header is repeated, its combined value consists of all values separated by comma, concatenated in order as received.
+Following example shows how to parse headers:
+
+.. code-block:: C
+
+ #include <http/http_header_names.h>
+ if (msg.data.headers_len)
+ {
+ u8 *headers = 0;
+ http_header_table_t *ht;
+ vec_validate (headers, msg.data.headers_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
+ msg.data.headers_len, headers);
+ ASSERT (rv == msg.data.headers_len);
+ if (http_parse_headers (headers, &ht))
+ {
+ /* your error handling */
+ }
+ /* get Accept header */
+ const char *accept_value = http_get_header (ht, http_header_name_str (HTTP_HEADER_ACCEPT));
+ if (accept_value)
+ {
+ /* do something interesting */
+ }
+ http_free_header_table (ht);
+ vec_free (headers);
+ }
+
+Finally application reads body (if any), which might be received in multiple pieces (depends on size), so we might need some state machine in ``builtin_app_rx_callback``.
+We will add following members to our session context structure:
+
+.. code-block:: C
+
+ typedef struct
+ {
+ /* ... */
+ u64 to_recv;
+ u8 *resp_body;
+ } session_ctx_t;
+
+First we prepare vector for response body, do it only once when you are reading metadata:
+
+.. code-block:: C
+
+ /* drop everything up to body */
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset);
+ ctx->to_recv = msg.data.body_len;
+ /* prepare vector for response body */
+ vec_validate (ctx->resp_body, msg.data.body_len - 1);
+ vec_reset_length (ctx->resp_body);
+
+Now we can start reading body content, following block of code could be executed multiple times:
+
+.. code-block:: C
+
+ /* dequeue */
+ u32 n_deq = svm_fifo_max_dequeue (ts->rx_fifo);
+ /* current offset */
+ u64 curr = vec_len (ctx->resp_body);
+ rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, ctx->resp_body + curr);
+ ASSERT (rv == n_deq);
+ /* update length of the vector */
+ vec_set_len (ctx->resp_body, curr + n_deq);
+ /* update number of remaining bytes to receive */
+ ctx->to_recv -= rv;
+ /* check if all data received */
+ if (ctx->to_recv == 0)
+ {
+ /* we are done */
+ /* send 200 OK response */
+ }
+
+Sending data
+""""""""""""""
+
+When server application sends response back to HTTP layer it starts with message metadata, followed by optional serialized headers and finally body (if any).
+
+Application should set following items:
+
+* Status code
+* target form
+* header section offset and length
+* body offset and length
+
+Application could pass headers back to HTTP layer. Header list is created dynamically as vector of ``http_header_t``,
+where we store only pointers to buffers (zero copy).
+Well known header names are predefined.
+The list is serialized just before you send buffer to HTTP layer.
+
+.. note::
+ Following headers are added at protocol layer and **MUST NOT** be set by application: Date, Server, Content-Length
+
+Following example shows how to create headers section:
+
+.. code-block:: C
+
+ #include <http/http.h>
+ #include <http/http_header_names.h>
+ #include <http/http_content_types.h>
+ http_header_t *resp_headers = 0;
+ u8 *headers_buf = 0;
+ http_add_header (resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (HTTP_CONTENT_TEXT_HTML));
+ http_add_header (resp_headers,
+ http_header_name_token (HTTP_HEADER_CACHE_CONTROL),
+ http_token_lit ("max-age=600"));
+ http_add_header (resp_headers,
+ http_header_name_token (HTTP_HEADER_LOCATION),
+ (const char *) redirect, vec_len (redirect));
+ headers_buf = http_serialize_headers (resp_headers);
+
+The example below show how to create and send response HTTP message metadata:
+
+.. code-block:: C
+
+ http_msg_t msg;
+ msg.type = HTTP_MSG_REPLY;
+ msg.code = HTTP_STATUS_MOVED
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = vec_len (headers_buf);
+ msg.data.type = HTTP_MSG_DATA_INLINE;
+ msg.data.body_len = vec_len (tx_buf);
+ msg.data.body_offset = msg.data.headers_len;
+ msg.data.len = msg.data.body_len + msg.data.headers_len;
+ ts = session_get (hs->vpp_session_index, hs->thread_index);
+ rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
+ ASSERT (rv == sizeof (msg));
+
+Next you will send your serialized headers:
+
+.. code-block:: C
+
+ rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf);
+ ASSERT (rv == msg.data.headers_len);
+ vec_free (headers_buf);
+
+Finally application sends response body:
+
+.. code-block:: C
+
+ rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (tx_buf), tx_buf);
+ if (rv != vec_len (hs->tx_buf))
+ {
+ hs->tx_offset = rv;
+ svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ }
+ else
+ {
+ vec_free (tx_buf);
+ }
+ if (svm_fifo_set_event (ts->tx_fifo))
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
+
+Examples above shows how to send body and headers by copy, alternatively you could pass them as pointer:
+
+.. code-block:: C
+
+ msg.data.type = HTTP_MSG_DATA_PTR;
+ /* code omitted for brevity */
+ if (msg.data.headers_len)
+ {
+ uword headers = pointer_to_uword (headers_buf);
+ rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (headers), (u8 *) &headers);
+ ASSERT (rv == sizeof (headers));
+ }
+ uword data = pointer_to_uword (tx_buf);
+ rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (data), (u8 *) &data);
+ ASSERT (rv == sizeof (data));
+
+In this case you need to free data when you receive next request or when session is closed.
+
+
+Client application
+^^^^^^^^^^^^^^^^^^
+
+Client application opens connection with vnet URI where transport protocol is set to ``http``.
+
+Sending data
+""""""""""""""
+
+HTTP request is sent when connection is successfully established in ``session_connected_callback``.
+
+When client application sends message to HTTP layer it starts with message metadata, followed by request target, optional headers and body (if any) buffers.
+
+Application should set following items:
+
+* HTTP method
+* target form, offset and length
+* header section offset and length
+* body offset and length
+
+Application could pass headers to HTTP layer. Header list is created dynamically as vector of ``http_header_t``,
+where we store only pointers to buffers (zero copy).
+Well known header names are predefined.
+The list is serialized just before you send buffer to HTTP layer.
+
+.. note::
+ Following headers are added at protocol layer and **MUST NOT** be set by application: Host, User-Agent
+
+
+The example below shows how to create headers section:
+
+.. code-block:: C
+
+ #include <http/http.h>
+ #include <http/http_header_names.h>
+ #include <http/http_content_types.h>
+ http_header_t *req_headers = 0;
+ u8 *headers_buf = 0;
+ http_add_header (req_headers,
+ http_header_name_token (HTTP_HEADER_ACCEPT),
+ http_content_type_token (HTTP_CONTENT_TEXT_HTML));
+ headers_buf = http_serialize_headers (req_headers);
+ vec_free (hs->req_headers);
+
+Following example shows how to set message metadata:
+
+.. code-block:: C
+
+ http_msg_t msg;
+ msg.type = HTTP_MSG_REQUEST;
+ msg.method_type = HTTP_REQ_GET;
+ msg.data.headers_offset = 0;
+ /* request target */
+ msg.data.target_form = HTTP_TARGET_ORIGIN_FORM;
+ msg.data.target_path_offset = 0;
+ msg.data.target_path_len = vec_len (target);
+ /* custom headers */
+ msg.data.headers_offset = msg.data.target_path_len;
+ msg.data.headers_len = vec_len (headers_buf);
+ /* no request body because we are doing GET request */
+ msg.data.body_len = 0;
+ /* data type and total length */
+ msg.data.type = HTTP_MSG_DATA_INLINE;
+ msg.data.len = msg.data.target_path_len + msg.data.headers_len + msg.data.body_len;
+
+Finally application sends everything to HTTP layer:
+
+.. code-block:: C
+
+ svm_fifo_seg_t segs[3] = { { (u8 *) &msg, sizeof (msg) }, /* message metadata */
+ { target, vec_len (target) }, /* request target */
+ { headers_buf, vec_len (headers_buf) } }; /* serialized headers */
+ rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 3, 0 /* allow partial */);
+ vec_free (headers_buf);
+ if (rv < 0 || rv != sizeof (msg) + msg.data.len)
+ {
+ clib_warning ("failed app enqueue");
+ return -1;
+ }
+ if (svm_fifo_set_event (as->tx_fifo))
+ session_program_tx_io_evt (as->handle, SESSION_IO_EVT_TX);
+
+Examples above shows how to send buffers by copy, alternatively you could pass them as pointer:
+
+.. code-block:: C
+
+ msg.data.type = HTTP_MSG_DATA_PTR;
+ msg.method_type = HTTP_REQ_POST;
+ msg.data.body_len = vec_len (data);
+ /* code omitted for brevity */
+ uword target = pointer_to_uword (target);
+ uword headers = pointer_to_uword (headers_buf);
+ uword body = pointer_to_uword (data);
+ svm_fifo_seg_t segs[4] = {
+ { (u8 *) &msg, sizeof (msg) },
+ { (u8 *) &target, sizeof (target) },
+ { (u8 *) &headers, sizeof (headers) },
+ { (u8 *) &body, sizeof (body) },
+ };
+ rv = svm_fifo_enqueue_segments (s->tx_fifo, segs, 4, 0 /* allow partial */);
+ ASSERT (rv == (sizeof (msg) + sizeof (target) + sizeof (headers) + sizeof (body)));
+
+In this case you need to free data when you receive response or when session is closed.
+
+Receiving data
+""""""""""""""
+
+HTTP plugin sends message header with metadata for parsing, in form of offset and length, followed by all data bytes as received from transport.
+
+Application will get pre-parsed following items:
+
+* status code
+* header section offset and length
+* body offset and length
+
+The example below reads HTTP message header in ``builtin_app_rx_callback``, which is first step application should do:
+
+.. code-block:: C
+
+ #include <http/http.h>
+ http_msg_t msg;
+ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
+ ASSERT (rv == sizeof (msg));
+
+As next step application might validate message type and status code:
+
+.. code-block:: C
+
+ if (msg.type != HTTP_MSG_REPLY)
+ {
+ /* your error handling */
+ }
+ if (msg.code != HTTP_STATUS_OK)
+ {
+ /* your error handling */
+ /* of course you can continue with steps bellow */
+ /* you might be interested in some headers or body content (if any) */
+ }
+
+Headers are parsed using a generic algorithm, independent of the individual header names.
+When header is repeated, its combined value consists of all values separated by comma, concatenated in order as received.
+Following example shows how to parse headers:
+
+.. code-block:: C
+
+ #include <http/http_header_names.h>
+ if (msg.data.headers_len)
+ {
+ u8 *headers = 0;
+ http_header_table_t *ht;
+ vec_validate (headers, msg.data.headers_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
+ msg.data.headers_len, headers);
+ ASSERT (rv == msg.data.headers_len);
+ if (http_parse_headers (headers, &ht))
+ {
+ /* your error handling */
+ }
+ /* get Content-Type header */
+ const char *content_type = http_get_header (ht, http_header_name_str (HTTP_HEADER_CONTENT_TYPE));
+ if (content_type)
+ {
+ /* do something interesting */
+ }
+ http_free_header_table (ht);
+ vec_free (headers);
+ }
+
+Finally application reads body, which might be received in multiple pieces (depends on size), so we might need some state machine in ``builtin_app_rx_callback``.
+We will add following members to our session context structure:
+
+.. code-block:: C
+
+ typedef struct
+ {
+ /* ... */
+ u64 to_recv;
+ u8 *resp_body;
+ } session_ctx_t;
+
+First we prepare vector for response body, do it only once when you are reading metadata:
+
+.. code-block:: C
+
+ /* drop everything up to body */
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset);
+ ctx->to_recv = msg.data.body_len;
+ /* prepare vector for response body */
+ vec_validate (ctx->resp_body, msg.data.body_len - 1);
+ vec_reset_length (ctx->resp_body);
+
+Now we can start reading body content, following block of code could be executed multiple times:
+
+.. code-block:: C
+
+ /* dequeue */
+ u32 max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
+ u32 n_deq = clib_min (to_recv, max_deq);
+ /* current offset */
+ u64 curr = vec_len (ctx->resp_body);
+ rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, ctx->resp_body + curr);
+ if (rv < 0 || rv != n_deq)
+ {
+ /* your error handling */
+ }
+ /* update length of the vector */
+ vec_set_len (ctx->resp_body, curr + n_deq);
+ /* update number of remaining bytes to receive */
+ ASSERT (to_recv >= rv);
+ ctx->to_recv -= rv;
+ /* check if all data received */
+ if (ctx->to_recv == 0)
+ {
+ /* we are done */
+ /* close the session if you don't want to send another request */
+ /* and update state machine... */
+ }
diff --git a/src/plugins/http/http_status_codes.h b/src/plugins/http/http_status_codes.h
new file mode 100644
index 00000000000..100095c8f42
--- /dev/null
+++ b/src/plugins/http/http_status_codes.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_PLUGINS_HTTP_HTTP_STATUS_CODES_H_
+#define SRC_PLUGINS_HTTP_HTTP_STATUS_CODES_H_
+
+#include <http/http.h>
+
+static const char *http_status_code_str[] = {
+#define _(c, s, str) str,
+ foreach_http_status_code
+#undef _
+};
+
+static inline u8 *
+format_http_status_code (u8 *s, va_list *va)
+{
+ http_status_code_t status_code = va_arg (*va, http_status_code_t);
+ if (status_code < HTTP_N_STATUS)
+ s = format (s, "%s", http_status_code_str[status_code]);
+ else
+ s = format (s, "invalid status code %d", status_code);
+ return s;
+}
+
+#endif /* SRC_PLUGINS_HTTP_HTTP_STATUS_CODES_H_ */
diff --git a/src/plugins/http/http_test.c b/src/plugins/http/http_test.c
new file mode 100644
index 00000000000..1f2f21dd19a
--- /dev/null
+++ b/src/plugins/http/http_test.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#include <http/http.h>
+
+static clib_error_t *
+test_http_authority_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ u8 *target = 0;
+ http_uri_t authority;
+ int rv;
+
+ if (!unformat (input, "%v", &target))
+ return clib_error_return (0, "error: no input provided");
+
+ rv = http_parse_authority_form_target (target, &authority);
+ vec_free (target);
+ if (rv)
+ return clib_error_return (0, "error: parsing failed");
+
+ target = http_serialize_authority_form_target (&authority);
+ vlib_cli_output (vm, "%v", target);
+ vec_free (target);
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (test_http_authority_command) = {
+ .path = "test http authority-form",
+ .short_help = "test dns authority-form",
+ .function = test_http_authority_command_fn,
+};
diff --git a/src/plugins/http/http_timer.c b/src/plugins/http/http_timer.c
index 42fe69076fe..5ee8efc8551 100644
--- a/src/plugins/http/http_timer.c
+++ b/src/plugins/http/http_timer.c
@@ -71,6 +71,8 @@ http_timers_init (vlib_main_t *vm, http_conn_timeout_fn *cb_fn)
http_tw_ctx_t *twc = &http_tw_ctx;
vlib_node_t *n;
+ ASSERT (twc->tw.timers == 0);
+
tw_timer_wheel_init_2t_1w_2048sl (&twc->tw, http_timer_process_expired_cb,
1.0 /* timer interval */, ~0);
clib_spinlock_init (&twc->tw_lock);
diff --git a/src/plugins/http_static/builtinurl/json_urls.c b/src/plugins/http_static/builtinurl/json_urls.c
index 808893aac79..19c5245e4b2 100644
--- a/src/plugins/http_static/builtinurl/json_urls.c
+++ b/src/plugins/http_static/builtinurl/json_urls.c
@@ -20,77 +20,68 @@ hss_url_handler_rc_t
handle_get_version (hss_url_handler_args_t *args)
{
u8 *s = 0;
+ unformat_input_t input;
+ int verbose = 0;
+
+ if (args->query)
+ {
+ unformat_init_vector (&input, args->query);
+ if (unformat (&input, "verbose="))
+ {
+ if (unformat (&input, "true"))
+ verbose = 1;
+ }
+ }
s = format (s, "{\"vpp_details\": {");
s = format (s, " \"version\": \"%s\",", VPP_BUILD_VER);
+ if (verbose)
+ {
+ s = format (s, " \"build_by\": \"%s\",", VPP_BUILD_USER);
+ s = format (s, " \"build_host\": \"%s\",", VPP_BUILD_HOST);
+ s = format (s, " \"build_dir\": \"%s\",", VPP_BUILD_TOPDIR);
+ }
s = format (s, " \"build_date\": \"%s\"}}\r\n", VPP_BUILD_DATE);
args->data = s;
args->data_len = vec_len (s);
+ args->ct = HTTP_CONTENT_APP_JSON;
args->free_vec_data = 1;
return HSS_URL_HANDLER_OK;
}
-void
-trim_path_from_request (u8 *s, char *path)
-{
- u8 *cp;
- int trim_length = strlen (path) + 1 /* remove '?' */;
-
- /* Get rid of the path and question-mark */
- vec_delete (s, trim_length, 0);
-
- /* Tail trim irrelevant browser info */
- cp = s;
- while ((cp - s) < vec_len (s))
- {
- if (*cp == ' ')
- {
- /*
- * Makes request a vector which happens to look
- * like a c-string.
- */
- *cp = 0;
- vec_set_len (s, cp - s);
- break;
- }
- cp++;
- }
-}
-
hss_url_handler_rc_t
handle_get_interface_stats (hss_url_handler_args_t *args)
{
u8 *s = 0, *stats = 0;
- uword *p;
- u32 *sw_if_indices = 0;
+ u32 sw_if_index, *sw_if_indices = 0;
vnet_hw_interface_t *hi;
vnet_sw_interface_t *si;
char *q = "\"";
int i;
int need_comma = 0;
+ unformat_input_t input;
u8 *format_vnet_sw_interface_cntrs (u8 * s, vnet_interface_main_t * im,
vnet_sw_interface_t * si, int json);
vnet_main_t *vnm = vnet_get_main ();
vnet_interface_main_t *im = &vnm->interface_main;
/* Get stats for a single interface via http POST */
- if (args->reqtype == HTTP_REQ_POST)
+ if (args->req_type == HTTP_REQ_POST)
{
- trim_path_from_request (args->request, "interface_stats.json");
-
+ unformat_init_vector (&input, args->req_data);
/* Find the sw_if_index */
- p = hash_get (im->hw_interface_by_name, args->request);
- if (!p)
+ if (!unformat (&input, "%U", unformat_vnet_sw_interface, vnm,
+ &sw_if_index))
{
s = format (s, "{\"interface_stats\": {[\n");
- s = format (s, " \"name\": \"%s\",", args->request);
+ s = format (s, " \"name\": \"%s\",", args->req_data);
s = format (s, " \"error\": \"%s\"", "UnknownInterface");
s = format (s, "]}\n");
goto out;
}
- vec_add1 (sw_if_indices, p[0]);
+ vec_add1 (sw_if_indices, sw_if_index);
}
else /* default, HTTP_BUILTIN_METHOD_GET */
{
@@ -127,6 +118,7 @@ handle_get_interface_stats (hss_url_handler_args_t *args)
out:
args->data = s;
args->data_len = vec_len (s);
+ args->ct = HTTP_CONTENT_APP_JSON;
args->free_vec_data = 1;
vec_free (sw_if_indices);
vec_free (stats);
@@ -167,6 +159,7 @@ handle_get_interface_list (hss_url_handler_args_t *args)
args->data = s;
args->data_len = vec_len (s);
+ args->ct = HTTP_CONTENT_APP_JSON;
args->free_vec_data = 1;
return HSS_URL_HANDLER_OK;
}
diff --git a/src/plugins/http_static/http_cache.c b/src/plugins/http_static/http_cache.c
index 8b9751b7f78..2e63e335d47 100644
--- a/src/plugins/http_static/http_cache.c
+++ b/src/plugins/http_static/http_cache.c
@@ -17,6 +17,8 @@
#include <vppinfra/bihash_template.c>
#include <vppinfra/unix.h>
#include <vlib/vlib.h>
+#include <sys/stat.h>
+#include <vppinfra/time_range.h>
static void
hss_cache_lock (hss_cache_t *hc)
@@ -153,7 +155,7 @@ lru_update (hss_cache_t *hc, hss_cache_entry_t *ep, f64 now)
static void
hss_cache_attach_entry (hss_cache_t *hc, u32 ce_index, u8 **data,
- u64 *data_len)
+ u64 *data_len, u8 **last_modified)
{
hss_cache_entry_t *ce;
@@ -162,6 +164,7 @@ hss_cache_attach_entry (hss_cache_t *hc, u32 ce_index, u8 **data,
ce->inuse++;
*data = ce->data;
*data_len = vec_len (ce->data);
+ *last_modified = ce->last_modified;
/* Update the cache entry, mark it in-use */
lru_update (hc, ce, vlib_time_now (vlib_get_main ()));
@@ -209,16 +212,15 @@ hss_cache_lookup (hss_cache_t *hc, u8 *path)
u32
hss_cache_lookup_and_attach (hss_cache_t *hc, u8 *path, u8 **data,
- u64 *data_len)
+ u64 *data_len, u8 **last_modified)
{
u32 ce_index;
-
/* Make sure nobody removes the entry while we look it up */
hss_cache_lock (hc);
ce_index = hss_cache_lookup (hc, path);
if (ce_index != ~0)
- hss_cache_attach_entry (hc, ce_index, data, data_len);
+ hss_cache_attach_entry (hc, ce_index, data, data_len, last_modified);
hss_cache_unlock (hc);
@@ -260,6 +262,7 @@ hss_cache_do_evictions (hss_cache_t *hc)
hc->cache_evictions++;
vec_free (ce->filename);
vec_free (ce->data);
+ vec_free (ce->last_modified);
if (hc->debug_level > 1)
clib_warning ("pool put index %d", ce - hc->cache_pool);
@@ -271,13 +274,15 @@ hss_cache_do_evictions (hss_cache_t *hc)
}
u32
-hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len)
+hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len,
+ u8 **last_modified)
{
BVT (clib_bihash_kv) kv;
hss_cache_entry_t *ce;
clib_error_t *error;
u8 *file_data;
u32 ce_index;
+ struct stat dm;
hss_cache_lock (hc);
@@ -298,11 +303,17 @@ hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len)
pool_get_zero (hc->cache_pool, ce);
ce->filename = vec_dup (path);
ce->data = file_data;
+ if (stat ((char *) path, &dm) == 0)
+ {
+ ce->last_modified =
+ format (0, "%U GMT", format_clib_timebase_time, (f64) dm.st_mtime);
+ }
/* Attach cache entry without additional lock */
ce->inuse++;
*data = file_data;
*data_len = vec_len (file_data);
+ *last_modified = ce->last_modified;
lru_add (hc, ce, vlib_time_now (vlib_get_main ()));
hc->cache_size += vec_len (ce->data);
@@ -364,6 +375,7 @@ hss_cache_clear (hss_cache_t *hc)
hc->cache_evictions++;
vec_free (ce->filename);
vec_free (ce->data);
+ vec_free (ce->last_modified);
if (hc->debug_level > 1)
clib_warning ("pool put index %d", ce - hc->cache_pool);
pool_put (hc->cache_pool, ce);
@@ -421,19 +433,19 @@ format_hss_cache (u8 *s, va_list *args)
{
s = format (s, "cache size %lld bytes, limit %lld bytes, evictions %lld",
hc->cache_size, hc->cache_limit, hc->cache_evictions);
- return 0;
+ return s;
}
vm = vlib_get_main ();
now = vlib_time_now (vm);
- s = format (s, "%U", format_hss_cache_entry, 0 /* header */, now);
+ s = format (s, "%U\n", format_hss_cache_entry, 0 /* header */, now);
for (index = hc->first_index; index != ~0;)
{
ce = pool_elt_at_index (hc->cache_pool, index);
index = ce->next_index;
- s = format (s, "%U", format_hss_cache_entry, ce, now);
+ s = format (s, "%U\n", format_hss_cache_entry, ce, now);
}
s = format (s, "%40s%12lld", "Total Size", hc->cache_size);
diff --git a/src/plugins/http_static/http_cache.h b/src/plugins/http_static/http_cache.h
index a89ed5e7e94..21f71a924d5 100644
--- a/src/plugins/http_static/http_cache.h
+++ b/src/plugins/http_static/http_cache.h
@@ -22,6 +22,9 @@ typedef struct hss_cache_entry_
{
/** Name of the file */
u8 *filename;
+ /** Last modified date, format:
+ * <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT */
+ u8 *last_modified;
/** Contents of the file, as a u8 * vector */
u8 *data;
/** Last time the cache entry was used */
@@ -58,9 +61,9 @@ typedef struct hss_cache_
} hss_cache_t;
u32 hss_cache_lookup_and_attach (hss_cache_t *hc, u8 *path, u8 **data,
- u64 *data_len);
+ u64 *data_len, u8 **last_modified);
u32 hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data,
- u64 *data_len);
+ u64 *data_len, u8 **last_modified);
void hss_cache_detach_entry (hss_cache_t *hc, u32 ce_index);
u32 hss_cache_clear (hss_cache_t *hc);
void hss_cache_init (hss_cache_t *hc, uword cache_size, u8 debug_level);
diff --git a/src/plugins/http_static/http_static.api b/src/plugins/http_static/http_static.api
index 4d6d8bfe9b5..dd4f513a420 100644
--- a/src/plugins/http_static/http_static.api
+++ b/src/plugins/http_static/http_static.api
@@ -2,7 +2,8 @@
/** \file
This file defines static http server control-plane API messages
*/
-option version = "2.1.0";
+
+option version = "2.2.0";
/** \brief Configure and enable the static http server
@param client_index - opaque cookie to identify the sender
@@ -16,6 +17,39 @@ option version = "2.1.0";
*/
autoreply define http_static_enable {
+ option deprecated;
+
+ /* Client identifier, set from api_main.my_client_index */
+ u32 client_index;
+
+ /* Arbitrary context, so client can match reply to request */
+ u32 context;
+ /* Typical options */
+ u32 fifo_size;
+ u32 cache_size_limit;
+ /* Unusual options */
+ u32 prealloc_fifos;
+ u32 private_segment_size;
+
+ /* Root of the html path */
+ string www_root[256];
+ /* The bind URI */
+ string uri[256];
+};
+
+/** \brief Configure and enable the static http server
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param fifo_size - size (in bytes) of the session FIFOs
+ @param cache_size_limit - size (in bytes) of the in-memory file data cache
+ @param max_age - how long a response is considered fresh (in seconds)
+ @param prealloc_fifos - number of preallocated fifos (usually 0)
+ @param private_segment_size - fifo segment size (usually 0)
+ @param www_root - html root path
+ @param uri - bind URI, defaults to "tcp://0.0.0.0/80"
+*/
+
+autoreply define http_static_enable_v2 {
/* Client identifier, set from api_main.my_client_index */
u32 client_index;
@@ -24,6 +58,7 @@ autoreply define http_static_enable {
/* Typical options */
u32 fifo_size;
u32 cache_size_limit;
+ u32 max_age [default=600];
/* Unusual options */
u32 prealloc_fifos;
u32 private_segment_size;
diff --git a/src/plugins/http_static/http_static.c b/src/plugins/http_static/http_static.c
index 8f8fe37b7c1..967b8474af8 100644
--- a/src/plugins/http_static/http_static.c
+++ b/src/plugins/http_static/http_static.c
@@ -66,7 +66,7 @@ hss_register_url_handler (hss_url_handler_fn fp, const char *url,
*/
static int
hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
- u32 private_segment_size, u8 *www_root, u8 *uri)
+ u32 private_segment_size, u8 *www_root, u8 *uri, u32 max_age)
{
hss_main_t *hsm = &hss_main;
int rv;
@@ -77,6 +77,7 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
hsm->private_segment_size = private_segment_size;
hsm->www_root = format (0, "%s%c", www_root, 0);
hsm->uri = format (0, "%s%c", uri, 0);
+ hsm->max_age = max_age;
if (vec_len (hsm->www_root) < 2)
return VNET_API_ERROR_INVALID_VALUE;
@@ -84,7 +85,10 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
if (hsm->app_index != ~0)
return VNET_API_ERROR_APP_ALREADY_ATTACHED;
- vnet_session_enable_disable (hsm->vlib_main, 1 /* turn on TCP, etc. */);
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (hsm->vlib_main, &args);
rv = hss_create (hsm->vlib_main);
switch (rv)
@@ -110,14 +114,33 @@ static void vl_api_http_static_enable_t_handler
mp->uri[ARRAY_LEN (mp->uri) - 1] = 0;
mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0;
- rv =
- hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit),
- ntohl (mp->prealloc_fifos),
- ntohl (mp->private_segment_size), mp->www_root, mp->uri);
+ rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit),
+ ntohl (mp->prealloc_fifos),
+ ntohl (mp->private_segment_size), mp->www_root, mp->uri,
+ HSS_DEFAULT_MAX_AGE);
REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_REPLY);
}
+/* API message handler */
+static void
+vl_api_http_static_enable_v2_t_handler (vl_api_http_static_enable_v2_t *mp)
+{
+ vl_api_http_static_enable_v2_reply_t *rmp;
+ hss_main_t *hsm = &hss_main;
+ int rv;
+
+ mp->uri[ARRAY_LEN (mp->uri) - 1] = 0;
+ mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0;
+
+ rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit),
+ ntohl (mp->prealloc_fifos),
+ ntohl (mp->private_segment_size), mp->www_root, mp->uri,
+ ntohl (mp->max_age));
+
+ REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V2_REPLY);
+}
+
#include <http_static/http_static.api.c>
static clib_error_t *
hss_api_init (vlib_main_t *vm)
diff --git a/src/plugins/http_static/http_static.h b/src/plugins/http_static/http_static.h
index 2850d356b74..bee79090d2b 100644
--- a/src/plugins/http_static/http_static.h
+++ b/src/plugins/http_static/http_static.h
@@ -23,6 +23,8 @@
#include <vppinfra/error.h>
#include <http_static/http_cache.h>
+#define HSS_DEFAULT_MAX_AGE 600
+
/** @file http_static.h
* Static http server definitions
*/
@@ -45,13 +47,15 @@ typedef struct
/** Data length */
u64 data_len;
/** Current data send offset */
- u32 data_offset;
+ u64 data_offset;
/** Need to free data in detach_cache_entry */
int free_data;
/** File cache pool index */
u32 cache_pool_index;
- /** Content type, e.g. text, text/javascript, etc. */
- http_content_type_t content_type;
+ /** Response header list */
+ http_header_t *resp_headers;
+ /** Serialized headers to send */
+ u8 *headers_buf;
} hss_session_t;
typedef struct hss_session_handle_
@@ -79,8 +83,9 @@ typedef struct hss_url_handler_args_
/* Request args */
struct
{
- u8 *request;
- http_req_method_t reqtype;
+ u8 *query;
+ u8 *req_data;
+ http_req_method_t req_type;
};
/* Reply args */
@@ -90,6 +95,7 @@ typedef struct hss_url_handler_args_
uword data_len;
u8 free_vec_data;
http_status_code_t sc;
+ http_content_type_t ct;
};
};
} hss_url_handler_args_t;
@@ -152,6 +158,10 @@ typedef struct
u8 enable_url_handlers;
/** Max cache size before LRU occurs */
u64 cache_size;
+ /** How long a response is considered fresh (in seconds) */
+ u32 max_age;
+ /** Formatted max_age: "max-age=xyz" */
+ u8 *max_age_formatted;
/** hash table of file extensions to mime types string indices */
uword *mime_type_indices_by_file_extensions;
diff --git a/src/plugins/http_static/http_static_test.c b/src/plugins/http_static/http_static_test.c
index 3503a1b0812..f701c8b9ee7 100644
--- a/src/plugins/http_static/http_static_test.c
+++ b/src/plugins/http_static/http_static_test.c
@@ -18,6 +18,7 @@
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vppinfra/error.h>
+#include <http_static/http_static.h>
uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
@@ -126,6 +127,96 @@ api_http_static_enable (vat_main_t * vam)
return ret;
}
+static int
+api_http_static_enable_v2 (vat_main_t *vam)
+{
+ unformat_input_t *line_input = vam->input;
+ vl_api_http_static_enable_v2_t *mp;
+ u64 tmp;
+ u8 *www_root = 0;
+ u8 *uri = 0;
+ u32 prealloc_fifos = 0;
+ u32 private_segment_size = 0;
+ u32 fifo_size = 8 << 10;
+ u32 cache_size_limit = 1 << 20;
+ u32 max_age = HSS_DEFAULT_MAX_AGE;
+ int ret;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "www-root %s", &www_root))
+ ;
+ else if (unformat (line_input, "prealloc-fifos %d", &prealloc_fifos))
+ ;
+ else if (unformat (line_input, "private-segment-size %U",
+ unformat_memory_size, &tmp))
+ {
+ if (tmp >= 0x100000000ULL)
+ {
+ errmsg ("private segment size %llu, too large", tmp);
+ return -99;
+ }
+ private_segment_size = (u32) tmp;
+ }
+ else if (unformat (line_input, "fifo-size %U", unformat_memory_size,
+ &tmp))
+ {
+ if (tmp >= 0x100000000ULL)
+ {
+ errmsg ("fifo-size %llu, too large", tmp);
+ return -99;
+ }
+ fifo_size = (u32) tmp;
+ }
+ else if (unformat (line_input, "cache-size %U", unformat_memory_size,
+ &tmp))
+ {
+ if (tmp < (128ULL << 10))
+ {
+ errmsg ("cache-size must be at least 128kb");
+ return -99;
+ }
+ cache_size_limit = (u32) tmp;
+ }
+ else if (unformat (line_input, "max-age %d", &max_age))
+ ;
+ else if (unformat (line_input, "uri %s", &uri))
+ ;
+ else
+ {
+ errmsg ("unknown input `%U'", format_unformat_error, line_input);
+ return -99;
+ }
+ }
+
+ if (www_root == 0)
+ {
+ errmsg ("Must specify www-root");
+ return -99;
+ }
+
+ if (uri == 0)
+ uri = format (0, "tcp://0.0.0.0/80%c", 0);
+
+ /* Construct the API message */
+ M (HTTP_STATIC_ENABLE_V2, mp);
+ strncpy_s ((char *) mp->www_root, 256, (const char *) www_root, 256);
+ strncpy_s ((char *) mp->uri, 256, (const char *) uri, 256);
+ mp->fifo_size = ntohl (fifo_size);
+ mp->cache_size_limit = ntohl (cache_size_limit);
+ mp->prealloc_fifos = ntohl (prealloc_fifos);
+ mp->private_segment_size = ntohl (private_segment_size);
+ mp->max_age = ntohl (max_age);
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
#include <http_static/http_static.api_test.c>
/*
diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c
index 040cdca9d7a..9d9a68f7d02 100644
--- a/src/plugins/http_static/static_server.c
+++ b/src/plugins/http_static/static_server.c
@@ -19,6 +19,9 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+
/** @file static_server.c
* Static http server, sufficient to serve .html / .css / .js content.
*/
@@ -83,48 +86,84 @@ start_send_data (hss_session_t *hs, http_status_code_t status)
{
http_msg_t msg;
session_t *ts;
+ u8 *headers_buf = 0;
+ u32 n_enq;
+ u64 to_send;
int rv;
ts = session_get (hs->vpp_session_index, hs->thread_index);
+ if (vec_len (hs->resp_headers))
+ {
+ headers_buf = http_serialize_headers (hs->resp_headers);
+ vec_free (hs->resp_headers);
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = vec_len (headers_buf);
+ }
+ else
+ {
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = 0;
+ }
+
msg.type = HTTP_MSG_REPLY;
msg.code = status;
- msg.content_type = hs->content_type;
- msg.data.len = hs->data_len;
+ msg.data.body_len = hs->data_len;
+ msg.data.len = msg.data.body_len + msg.data.headers_len;
- if (hs->data_len > hss_main.use_ptr_thresh)
+ if (msg.data.len > hss_main.use_ptr_thresh)
{
msg.data.type = HTTP_MSG_DATA_PTR;
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
+ if (msg.data.headers_len)
+ {
+ hs->headers_buf = headers_buf;
+ uword headers = pointer_to_uword (hs->headers_buf);
+ rv =
+ svm_fifo_enqueue (ts->tx_fifo, sizeof (headers), (u8 *) &headers);
+ ASSERT (rv == sizeof (headers));
+ }
+
uword data = pointer_to_uword (hs->data);
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (data), (u8 *) &data);
- ASSERT (rv == sizeof (sizeof (data)));
+ ASSERT (rv == sizeof (data));
goto done;
}
msg.data.type = HTTP_MSG_DATA_INLINE;
+ msg.data.body_offset = msg.data.headers_len;
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
- if (!msg.data.len)
+ if (msg.data.headers_len)
+ {
+ rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf);
+ ASSERT (rv == msg.data.headers_len);
+ vec_free (headers_buf);
+ }
+
+ if (!msg.data.body_len)
goto done;
- rv = svm_fifo_enqueue (ts->tx_fifo, hs->data_len, hs->data);
+ to_send = hs->data_len;
+ n_enq = clib_min (svm_fifo_size (ts->tx_fifo), to_send);
+
+ rv = svm_fifo_enqueue (ts->tx_fifo, n_enq, hs->data);
- if (rv != hs->data_len)
+ if (rv < to_send)
{
- hs->data_offset = rv;
+ hs->data_offset = (rv > 0) ? rv : 0;
svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
}
done:
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
}
__clib_export void
@@ -142,6 +181,15 @@ hss_session_send_data (hss_url_handler_args_t *args)
hs->data = args->data;
hs->data_len = args->data_len;
hs->free_data = args->free_vec_data;
+
+ /* Set content type only if we have some response data */
+ if (hs->data_len)
+ {
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (args->ct));
+ }
+
start_send_data (hs, args->sc);
}
@@ -212,30 +260,27 @@ content_type_from_request (u8 *request)
static int
try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
- u8 *request)
+ u8 *target_path, u8 *target_query, u8 *data)
{
http_status_code_t sc = HTTP_STATUS_OK;
hss_url_handler_args_t args = {};
uword *p, *url_table;
- http_content_type_t type;
int rv;
- if (!hsm->enable_url_handlers || !request)
+ if (!hsm->enable_url_handlers || !target_path)
return -1;
/* zero-length? try "index.html" */
- if (vec_len (request) == 0)
+ if (vec_len (target_path) == 0)
{
- request = format (request, "index.html");
+ target_path = format (target_path, "index.html");
}
- type = content_type_from_request (request);
-
/* Look for built-in GET / POST handlers */
url_table =
(rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers;
- p = hash_get_mem (url_table, request);
+ p = hash_get_mem (url_table, target_path);
if (!p)
return -1;
@@ -244,10 +289,12 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
hs->cache_pool_index = ~0;
if (hsm->debug_level > 0)
- clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", request);
+ clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST",
+ target_path);
- args.reqtype = rt;
- args.request = request;
+ args.req_type = rt;
+ args.query = target_query;
+ args.req_data = data;
args.sh.thread_index = hs->thread_index;
args.sh.session_index = hs->session_index;
@@ -260,18 +307,25 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
if (rv == HSS_URL_HANDLER_ERROR)
{
clib_warning ("builtin handler %llx hit on %s '%s' but failed!", p[0],
- (rt == HTTP_REQ_GET) ? "GET" : "POST", request);
- sc = HTTP_STATUS_NOT_FOUND;
+ (rt == HTTP_REQ_GET) ? "GET" : "POST", target_path);
+ sc = HTTP_STATUS_BAD_GATEWAY;
}
hs->data = args.data;
hs->data_len = args.data_len;
hs->free_data = args.free_vec_data;
- hs->content_type = type;
+
+ /* Set content type only if we have some response data */
+ if (hs->data_len)
+ {
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (args.ct));
+ }
start_send_data (hs, sc);
- if (!hs->data)
+ if (!hs->data_len)
hss_session_disconnect_transport (hs);
return 0;
@@ -335,18 +389,20 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path)
}
redirect =
- format (0,
- "Location: http%s://%U%s%s\r\n\r\n",
- proto == TRANSPORT_PROTO_TLS ? "s" : "", format_ip46_address,
- &endpt.ip, endpt.is_ip4, print_port ? port_str : (u8 *) "", path);
+ format (0, "http%s://%U%s%s", proto == TRANSPORT_PROTO_TLS ? "s" : "",
+ format_ip46_address, &endpt.ip, endpt.is_ip4,
+ print_port ? port_str : (u8 *) "", path);
if (hsm->debug_level > 0)
clib_warning ("redirect: %s", redirect);
vec_free (port_str);
- hs->data = redirect;
- hs->data_len = vec_len (redirect);
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_LOCATION),
+ (const char *) redirect, vec_len (redirect));
+ hs->data = redirect; /* TODO: find better way */
+ hs->data_len = 0;
hs->free_data = 1;
return HTTP_STATUS_MOVED;
@@ -354,29 +410,28 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path)
static int
try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
- u8 *request)
+ u8 *target)
{
http_status_code_t sc = HTTP_STATUS_OK;
- u8 *path;
+ u8 *path, *sanitized_path;
u32 ce_index;
http_content_type_t type;
+ u8 *last_modified;
/* Feature not enabled */
if (!hsm->www_root)
return -1;
- type = content_type_from_request (request);
+ /* Remove dot segments to prevent path traversal */
+ sanitized_path = http_path_remove_dot_segments (target);
/*
* Construct the file to open
- * Browsers are capable of sporadically including a leading '/'
*/
- if (!request)
+ if (!target)
path = format (0, "%s%c", hsm->www_root, 0);
- else if (request[0] == '/')
- path = format (0, "%s%s%c", hsm->www_root, request, 0);
else
- path = format (0, "%s/%s%c", hsm->www_root, request, 0);
+ path = format (0, "%s/%s%c", hsm->www_root, sanitized_path, 0);
if (hsm->debug_level > 0)
clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", path);
@@ -386,8 +441,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
hs->data_offset = 0;
- ce_index =
- hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
+ ce_index = hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data,
+ &hs->data_len, &last_modified);
if (ce_index == ~0)
{
if (!file_path_is_valid (path))
@@ -406,8 +461,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
sc = try_index_file (hsm, hs, path);
goto done;
}
- ce_index =
- hss_cache_add_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
+ ce_index = hss_cache_add_and_attach (&hsm->cache, path, &hs->data,
+ &hs->data_len, &last_modified);
if (ce_index == ~0)
{
sc = HTTP_STATUS_INTERNAL_ERROR;
@@ -418,43 +473,61 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
hs->path = path;
hs->cache_pool_index = ce_index;
-done:
+ /* Set following headers only for happy path:
+ * Content-Type
+ * Cache-Control max-age
+ */
+ type = content_type_from_request (target);
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (type));
+ http_add_header (
+ &hs->resp_headers, http_header_name_token (HTTP_HEADER_CACHE_CONTROL),
+ (const char *) hsm->max_age_formatted, vec_len (hsm->max_age_formatted));
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_LAST_MODIFIED),
+ (const char *) last_modified, vec_len (last_modified));
- hs->content_type = type;
+done:
+ vec_free (sanitized_path);
start_send_data (hs, sc);
- if (!hs->data)
+ if (!hs->data_len)
hss_session_disconnect_transport (hs);
return 0;
}
-static int
-handle_request (hss_session_t *hs, http_req_method_t rt, u8 *request)
+static void
+handle_request (hss_session_t *hs, http_req_method_t rt, u8 *target_path,
+ u8 *target_query, u8 *data)
{
hss_main_t *hsm = &hss_main;
- if (!try_url_handler (hsm, hs, rt, request))
- return 0;
+ if (!try_url_handler (hsm, hs, rt, target_path, target_query, data))
+ return;
- if (!try_file_handler (hsm, hs, rt, request))
- return 0;
+ if (!try_file_handler (hsm, hs, rt, target_path))
+ return;
/* Handler did not find anything return 404 */
start_send_data (hs, HTTP_STATUS_NOT_FOUND);
hss_session_disconnect_transport (hs);
-
- return 0;
}
static int
hss_ts_rx_callback (session_t *ts)
{
hss_session_t *hs;
- u8 *request = 0;
+ u8 *target_path = 0, *target_query = 0, *data = 0;
http_msg_t msg;
int rv;
hs = hss_session_get (ts->thread_index, ts->opaque);
+ if (hs->free_data)
+ vec_free (hs->data);
+ hs->data = 0;
+ hs->resp_headers = 0;
+ vec_free (hs->headers_buf);
/* Read the http message header */
rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
@@ -463,26 +536,66 @@ hss_ts_rx_callback (session_t *ts)
if (msg.type != HTTP_MSG_REQUEST ||
(msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST))
{
- hs->data = 0;
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_ALLOW),
+ http_token_lit ("GET, POST"));
start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
- return 0;
+ goto done;
}
- /* Read request */
- if (msg.data.len)
+ if (msg.data.target_form != HTTP_TARGET_ORIGIN_FORM)
{
- vec_validate (request, msg.data.len - 1);
- rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request);
- ASSERT (rv == msg.data.len);
- /* request must be a proper C-string in addition to a vector */
- vec_add1 (request, 0);
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
}
- /* Find and send data */
- handle_request (hs, msg.method_type, request);
+ /* Read target path */
+ if (msg.data.target_path_len)
+ {
+ vec_validate (target_path, msg.data.target_path_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset,
+ msg.data.target_path_len, target_path);
+ ASSERT (rv == msg.data.target_path_len);
+ if (http_validate_abs_path_syntax (target_path, 0))
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
+ }
+ /* Target path must be a proper C-string in addition to a vector */
+ vec_add1 (target_path, 0);
+ }
- vec_free (request);
+ /* Read target query */
+ if (msg.data.target_query_len)
+ {
+ vec_validate (target_query, msg.data.target_query_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_query_offset,
+ msg.data.target_query_len, target_query);
+ ASSERT (rv == msg.data.target_query_len);
+ if (http_validate_query_syntax (target_query, 0))
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
+ }
+ }
+ /* Read body */
+ if (msg.data.body_len)
+ {
+ vec_validate (data, msg.data.body_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len,
+ data);
+ ASSERT (rv == msg.data.body_len);
+ }
+
+ /* Find and send data */
+ handle_request (hs, msg.method_type, target_path, target_query, data);
+
+done:
+ vec_free (target_path);
+ vec_free (target_query);
+ vec_free (data);
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len);
return 0;
}
@@ -490,7 +603,8 @@ static int
hss_ts_tx_callback (session_t *ts)
{
hss_session_t *hs;
- u32 to_send;
+ u32 n_enq;
+ u64 to_send;
int rv;
hs = hss_session_get (ts->thread_index, ts->opaque);
@@ -498,7 +612,9 @@ hss_ts_tx_callback (session_t *ts)
return 0;
to_send = hs->data_len - hs->data_offset;
- rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->data + hs->data_offset);
+ n_enq = clib_min (svm_fifo_size (ts->tx_fifo), to_send);
+
+ rv = svm_fifo_enqueue (ts->tx_fifo, n_enq, hs->data + hs->data_offset);
if (rv <= 0)
{
@@ -513,7 +629,7 @@ hss_ts_tx_callback (session_t *ts)
}
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
return 0;
}
@@ -607,6 +723,7 @@ hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf)
hs->data = 0;
hs->data_offset = 0;
hs->free_data = 0;
+ vec_free (hs->headers_buf);
vec_free (hs->path);
hss_session_free (hs);
@@ -757,6 +874,8 @@ hss_create (vlib_main_t *vm)
if (hsm->enable_url_handlers)
hss_url_handlers_init (hsm);
+ hsm->max_age_formatted = format (0, "max-age=%d", hsm->max_age);
+
return 0;
}
@@ -777,6 +896,7 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
hsm->private_segment_size = 0;
hsm->fifo_size = 0;
hsm->cache_size = 10 << 20;
+ hsm->max_age = HSS_DEFAULT_MAX_AGE;
/* Get a line of input. */
if (!unformat_user (input, unformat_line_input, line_input))
@@ -808,6 +928,8 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
;
else if (unformat (line_input, "url-handlers"))
hsm->enable_url_handlers = 1;
+ else if (unformat (line_input, "max-age %d", &hsm->max_age))
+ ;
else
{
error = clib_error_return (0, "unknown input `%U'",
@@ -836,7 +958,10 @@ no_input:
goto done;
}
- vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
if ((rv = hss_create (vm)))
{
@@ -865,8 +990,8 @@ VLIB_CLI_COMMAND (hss_create_command, static) = {
.path = "http static server",
.short_help =
"http static server www-root <path> [prealloc-fifos <nn>]\n"
- "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
- "[ptr-thresh <nn>] [url-handlers] [debug [nn]]\n",
+ "[private-segment-size <nnMG>] [fifo-size <nbytes>] [max-age <nseconds>]\n"
+ "[uri <uri>] [ptr-thresh <nn>] [url-handlers] [debug [nn]]\n",
.function = hss_create_command_fn,
};
@@ -876,7 +1001,7 @@ format_hss_session (u8 *s, va_list *args)
hss_session_t *hs = va_arg (*args, hss_session_t *);
int __clib_unused verbose = va_arg (*args, int);
- s = format (s, "\n path %s, data length %u, data_offset %u",
+ s = format (s, "\n path %s, data length %llu, data_offset %llu",
hs->path ? hs->path : (u8 *) "[none]", hs->data_len,
hs->data_offset);
return s;
diff --git a/src/plugins/ikev2/CMakeLists.txt b/src/plugins/ikev2/CMakeLists.txt
index 568271ed7d9..dd2b49d6651 100644
--- a/src/plugins/ikev2/CMakeLists.txt
+++ b/src/plugins/ikev2/CMakeLists.txt
@@ -27,6 +27,7 @@ add_vpp_plugin(ikev2
ikev2_crypto.c
ikev2_format.c
ikev2_payload.c
+ ikev2_handoff.c
API_FILES
ikev2_types.api
diff --git a/src/plugins/ikev2/ikev2.api b/src/plugins/ikev2/ikev2.api
index de276e7f3ea..e2ff8fb8268 100644
--- a/src/plugins/ikev2/ikev2.api
+++ b/src/plugins/ikev2/ikev2.api
@@ -658,6 +658,12 @@ counters ikev2 {
units "packets";
description "IKE AUTH SA requests received";
};
+ handoff {
+ severity info;
+ type counter64;
+ units "packets";
+ description "IKE packets handoff";
+ };
};
paths {
"/err/ikev2-ip4" "ike";
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index 9bea2c96d12..f66469a24d1 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -97,6 +97,7 @@ format_ikev2_gen_sa_error (u8 * s, va_list * args)
typedef enum
{
IKEV2_NEXT_IP4_LOOKUP,
+ IKEV2_NEXT_IP4_HANDOFF,
IKEV2_NEXT_IP4_ERROR_DROP,
IKEV2_IP4_N_NEXT,
} ikev2_ip4_next_t;
@@ -104,6 +105,7 @@ typedef enum
typedef enum
{
IKEV2_NEXT_IP6_LOOKUP,
+ IKEV2_NEXT_IP6_HANDOFF,
IKEV2_NEXT_IP6_ERROR_DROP,
IKEV2_IP6_N_NEXT,
} ikev2_ip6_next_t;
@@ -3187,6 +3189,7 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
+ u32 thread_index = vm->thread_index;
ikev2_stats_t _stats, *stats = &_stats;
int res;
@@ -3213,6 +3216,14 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
int ip_hdr_sz = 0;
int is_req = 0;
+ if (PREDICT_TRUE (thread_index != km->handoff_thread))
+ {
+ vlib_node_increment_counter (vm, node->node_index,
+ IKEV2_ERROR_HANDOFF, 1);
+
+ next[0] = is_ip4 ? IKEV2_NEXT_IP4_HANDOFF : IKEV2_NEXT_IP6_HANDOFF;
+ goto out;
+ }
if (natt)
{
u8 *ptr = vlib_buffer_get_current (b0);
@@ -3723,6 +3734,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
ikev2_delete_sa (ptd, sa0);
}
+
+ out:
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
&& (b0->flags & VLIB_BUFFER_IS_TRACED)))
{
@@ -3775,6 +3788,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = {
.n_next_nodes = IKEV2_IP4_N_NEXT,
.next_nodes = {
[IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip4-handoff",
[IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop",
},
};
@@ -3792,6 +3806,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip4_natt,static) = {
.n_next_nodes = IKEV2_IP4_N_NEXT,
.next_nodes = {
[IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip4-natt-handoff",
[IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop",
},
};
@@ -3809,6 +3824,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip6,static) = {
.n_next_nodes = IKEV2_IP6_N_NEXT,
.next_nodes = {
[IKEV2_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip6-handoff",
[IKEV2_NEXT_IP6_ERROR_DROP] = "error-drop",
},
};
@@ -5126,6 +5142,8 @@ ikev2_init (vlib_main_t * vm)
km->liveness_period = IKEV2_LIVENESS_PERIOD_CHECK;
km->liveness_max_retries = IKEV2_LIVENESS_RETRIES;
+ km->handoff_thread = vlib_num_workers () ? 1 : 0;
+
return 0;
}
@@ -5133,6 +5151,31 @@ VLIB_INIT_FUNCTION (ikev2_init) = {
.runs_after = VLIB_INITS ("ipsec_init", "ipsec_punt_init"),
};
+static clib_error_t *
+ikev2_config (vlib_main_t *vm, unformat_input_t *input)
+{
+ ikev2_main_t *km = &ikev2_main;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "handoff-thread %d", &km->handoff_thread))
+ {
+ if (km->handoff_thread > vlib_num_workers ())
+ {
+ return clib_error_return (0, "wrong handoff-thread %d",
+ km->handoff_thread);
+ }
+ }
+ else
+ return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+ input);
+ }
+
+ return 0;
+}
+
+VLIB_CONFIG_FUNCTION (ikev2_config, "ikev2");
+
static u8
ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa,
u8 del_old_ids)
@@ -5447,6 +5490,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa)
}
dp = sa->dst_port ? sa->dst_port : ikev2_get_port (sa);
+
ikev2_send_ike (km->vlib_main, src, dst, bi0, len, ikev2_get_port (sa), dp,
sa->sw_if_index);
}
@@ -5625,6 +5669,15 @@ ikev2_lazy_init (ikev2_main_t *km)
if (!km->dns_resolve_name_ptr)
ikev2_log_error ("cannot load symbols from dns plugin");
+ km->handoff_ip4_fq_index =
+ vlib_frame_queue_main_init (ikev2_node_ip4.index, 0);
+
+ km->handoff_ip4_natt_fq_index =
+ vlib_frame_queue_main_init (ikev2_node_ip4_natt.index, 0);
+
+ km->handoff_ip6_fq_index =
+ vlib_frame_queue_main_init (ikev2_node_ip6.index, 0);
+
/* wake up ikev2 process */
vlib_process_signal_event (vlib_get_first_main (),
ikev2_mngr_process_node.index, 0, 0);
diff --git a/src/plugins/ikev2/ikev2_api.c b/src/plugins/ikev2/ikev2_api.c
index c9608aa660b..e09bde3cbe2 100644
--- a/src/plugins/ikev2/ikev2_api.c
+++ b/src/plugins/ikev2/ikev2_api.c
@@ -577,6 +577,7 @@ vl_api_ikev2_child_sa_dump_t_handler (vl_api_ikev2_child_sa_dump_t * mp)
vec_foreach (child, sa->childs)
{
u32 child_sa_index = child - sa->childs;
+ sai = ikev2_encode_sa_index (sai, tkm - im->per_thread_data);
send_child_sa (child, mp, child_sa_index, sai);
}
}
diff --git a/src/plugins/ikev2/ikev2_crypto.c b/src/plugins/ikev2/ikev2_crypto.c
index 3d4ad0a28ed..58167e2322e 100644
--- a/src/plugins/ikev2/ikev2_crypto.c
+++ b/src/plugins/ikev2/ikev2_crypto.c
@@ -481,15 +481,14 @@ ikev2_encrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
int
BN_bn2binpad (const BIGNUM * a, unsigned char *to, int tolen)
{
- int r = BN_bn2bin (a, to);
+ int r = BN_num_bytes (a);
ASSERT (tolen >= r);
int pad = tolen - r;
if (pad)
{
- vec_insert (to, pad, 0);
clib_memset (to, 0, pad);
- vec_dec_len (to, pad);
}
+ BN_bn2bin (a, to + pad);
return tolen;
}
#endif
diff --git a/src/plugins/ikev2/ikev2_handoff.c b/src/plugins/ikev2/ikev2_handoff.c
new file mode 100644
index 00000000000..8f55985bce8
--- /dev/null
+++ b/src/plugins/ikev2/ikev2_handoff.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <ikev2/ikev2_priv.h>
+
+extern ikev2_main_t ikev2_main;
+
+#define foreach_ikev2_handoff_error _ (CONGESTION_DROP, "congestion drop")
+
+typedef enum
+{
+#define _(sym, str) IKEV2_HANDOFF_ERROR_##sym,
+ foreach_ikev2_handoff_error
+#undef _
+ IKEV2_HANDOFF_N_ERROR,
+} ikev2_handoff_error_t;
+
+static char *ikev2_handoff_error_strings[] = {
+#define _(sym, string) string,
+ foreach_ikev2_handoff_error
+#undef _
+};
+
+typedef struct ikev2_handoff_trace_t_
+{
+ u32 current_worker_index;
+ u32 next_worker_index;
+} ikev2_handoff_trace_t;
+
+u8 *
+format_ikev2_handoff_trace (u8 *s, va_list *args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+
+ ikev2_handoff_trace_t *t = va_arg (*args, ikev2_handoff_trace_t *);
+ s = format (s, "ikev2 handoff %d to %d", t->current_worker_index,
+ t->next_worker_index);
+ return s;
+}
+
+static_always_inline uword
+ikev2_handoff_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, u32 fq_index)
+{
+ ikev2_main_t *km = &ikev2_main;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+ u16 thread_indices[VLIB_FRAME_SIZE], *ti;
+ u32 n_enq, n_left_from, *from;
+ u32 this_thread;
+
+ this_thread = vm->thread_index;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ vlib_get_buffers (vm, from, bufs, n_left_from);
+
+ b = bufs;
+ ti = thread_indices;
+
+ while (n_left_from > 0)
+ {
+ ti[0] = km->handoff_thread;
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ b[0]->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ikev2_handoff_trace_t *t =
+ vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->current_worker_index = this_thread;
+ t->next_worker_index = ti[0];
+ }
+ n_left_from--;
+ ti++;
+ b++;
+ }
+
+ n_enq = vlib_buffer_enqueue_to_thread (vm, node, fq_index, from,
+ thread_indices, frame->n_vectors, 1);
+
+ if (n_enq < frame->n_vectors)
+ vlib_node_increment_counter (vm, node->node_index,
+ IKEV2_HANDOFF_ERROR_CONGESTION_DROP,
+ frame->n_vectors - n_enq);
+ return n_enq;
+}
+
+/* Do worker handoff based on the ikev2's thread_index */
+VLIB_NODE_FN (ikev2_ip4_handoff)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
+{
+ ikev2_main_t *km = &ikev2_main;
+
+ return ikev2_handoff_inline (vm, node, from_frame, km->handoff_ip4_fq_index);
+}
+
+VLIB_NODE_FN (ikev2_ip4_natt_handoff)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
+{
+ ikev2_main_t *km = &ikev2_main;
+
+ return ikev2_handoff_inline (vm, node, from_frame,
+ km->handoff_ip4_natt_fq_index);
+}
+
+VLIB_NODE_FN (ikev2_ip6_handoff)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
+{
+ ikev2_main_t *km = &ikev2_main;
+
+ return ikev2_handoff_inline (vm, node, from_frame, km->handoff_ip6_fq_index);
+}
+
+VLIB_REGISTER_NODE (ikev2_ip4_handoff) = {
+ .name = "ikev2-ip4-handoff",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ikev2_handoff_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN(ikev2_handoff_error_strings),
+ .error_strings = ikev2_handoff_error_strings,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (ikev2_ip4_natt_handoff) = {
+ .name = "ikev2-ip4-natt-handoff",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ikev2_handoff_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN(ikev2_handoff_error_strings),
+ .error_strings = ikev2_handoff_error_strings,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (ikev2_ip6_handoff) = {
+ .name = "ikev2-ip6-handoff",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ikev2_handoff_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN(ikev2_handoff_error_strings),
+ .error_strings = ikev2_handoff_error_strings,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+};
diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h
index 0639809e9b1..96313182552 100644
--- a/src/plugins/ikev2/ikev2_priv.h
+++ b/src/plugins/ikev2/ikev2_priv.h
@@ -571,6 +571,12 @@ typedef struct
/* punt handle for IPsec NATT IPSEC_PUNT_IP4_SPI_UDP_0 reason */
vlib_punt_hdl_t punt_hdl;
+ /** Worker handoff */
+ u32 handoff_thread;
+ u32 handoff_ip4_fq_index;
+ u32 handoff_ip4_natt_fq_index;
+ u32 handoff_ip6_fq_index;
+
} ikev2_main_t;
extern ikev2_main_t ikev2_main;
diff --git a/src/plugins/linux-cp/lcp_interface.c b/src/plugins/linux-cp/lcp_interface.c
index e1f4a6a1d69..61665ad4146 100644
--- a/src/plugins/linux-cp/lcp_interface.c
+++ b/src/plugins/linux-cp/lcp_interface.c
@@ -258,7 +258,11 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
vec_validate_init_empty (lip_db_by_host, host_sw_if_index, INDEX_INVALID);
lip_db_by_phy[phy_sw_if_index] = lipi;
lip_db_by_host[host_sw_if_index] = lipi;
- hash_set (lip_db_by_vif, host_index, lipi);
+
+ if (clib_strcmp ((char *) ns, (char *) lcp_get_default_ns ()) == 0)
+ {
+ hash_set (lip_db_by_vif, host_index, lipi);
+ }
lip->lip_host_sw_if_index = host_sw_if_index;
lip->lip_phy_sw_if_index = phy_sw_if_index;
@@ -997,7 +1001,8 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
clib_max (1, lcp_get_default_num_queues (0 /* is_tx */)),
.num_tx_queues =
clib_max (1, lcp_get_default_num_queues (1 /* is_tx */)),
- .id = hw->hw_if_index,
+ .id = ~0,
+ .auto_id_offset = 4096,
.sw_if_index = ~0,
.rx_ring_sz = 256,
.tx_ring_sz = 256,
@@ -1094,7 +1099,7 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
* This controls whether the host can RX/TX.
*/
sw = vnet_get_sw_interface (vnm, phy_sw_if_index);
- lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (vif_index));
+ lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (phy_sw_if_index));
LCP_ITF_PAIR_INFO ("pair create: %U sw-flags %u hw-flags %u",
format_lcp_itf_pair, lip, sw->flags, hw->flags);
vnet_sw_interface_admin_up (vnm, host_sw_if_index);
diff --git a/src/plugins/mactime/builtins.c b/src/plugins/mactime/builtins.c
index c487d0375bf..f726d3c03ed 100644
--- a/src/plugins/mactime/builtins.c
+++ b/src/plugins/mactime/builtins.c
@@ -147,6 +147,7 @@ handle_get_mactime (hss_url_handler_args_t *args)
args->data = s;
args->data_len = vec_len (s);
+ args->ct = HTTP_CONTENT_APP_JSON;
args->free_vec_data = 1;
return HSS_URL_HANDLER_OK;
}
diff --git a/src/plugins/marvell/CMakeLists.txt b/src/plugins/marvell/CMakeLists.txt
deleted file mode 100644
index b48ac72aa08..00000000000
--- a/src/plugins/marvell/CMakeLists.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (c) 2018 Cisco and/or its affiliates.
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
- return()
-endif()
-
-find_path(MUSDK_INCLUDE_DIR NAMES mv_std.h)
-find_library(MUSDK_LIB NAMES libmusdk.a)
-
-if(MUSDK_INCLUDE_DIR AND MUSDK_LIB)
- get_filename_component(MUSDK_LIB_DIR ${MUSDK_LIB} DIRECTORY)
- set(MUSDK_LINK_FLAGS "-Wl,--whole-archive,${MUSDK_LIB_DIR}/libmusdk.a,--no-whole-archive")
- add_vpp_plugin(marvell
- SOURCES
- plugin.c
- pp2/cli.c
- pp2/format.c
- pp2/input.c
- pp2/output.c
- pp2/pp2.c
- pp2/pp2_api.c
-
- API_FILES
- pp2/pp2.api
-
- API_TEST_SOURCES
- pp2/pp2_test.c
-
- LINK_FLAGS
- ${MUSDK_LINK_FLAGS}
- )
- include_directories(${MUSDK_INCLUDE_DIR})
- message(STATUS "Found Marvell MUSDK in ${MUSDK_INCLUDE_DIR}")
-else()
- message(WARNING "Marvell MUSDK not found - marvell_plugin disabled")
-endif()
diff --git a/src/plugins/marvell/README.rst b/src/plugins/marvell/README.rst
deleted file mode 100644
index 19cf1c49d0e..00000000000
--- a/src/plugins/marvell/README.rst
+++ /dev/null
@@ -1,85 +0,0 @@
-Marvell device plugin
-=====================
-
-Overview
---------
-
-This plugins provides native device support for Marvell PP2 network
-device, by use of Marvell Usermode SDK
-(`MUSDK <https://github.com/MarvellEmbeddedProcessors/musdk-marvell>`__).
-Code is developed and tested on
-`MACCHIATObin <http://macchiatobin.net>`__ board.
-
-Prerequisites
--------------
-
-Plugins depends on installed MUSDK and Marvell provided linux
-`kernel <https://github.com/MarvellEmbeddedProcessors/linux-marvell>`__
-with MUSDK provided kernel patches (see ``patches/linux`` in musdk repo
-and relevant documentation. Kernel version used: **4.14.22
-armada-18.09.3** MUSDK version used: **armada-18.09.3** Following kernel
-modules from MUSDK must be loaded for plugin to work: \*
-``musdk_cma.ko`` \* ``mv_pp_uio.ko``
-
-Musdk 18.09.3 compilation steps
--------------------------------
-
-::
-
- ./bootstrap
- ./configure --prefix=/opt/vpp/external/aarch64/ CFLAGS="-Wno-error=unused-result -g -fPIC" --enable-shared=no
- sed -i -e 's/marvell,mv-pp-uio/generic-uio/' modules/pp2/mv_pp_uio.c
- sed -i -e 's/O_CREAT/O_CREAT, S_IRUSR | S_IWUSR/' src/lib/file_utils.c
- make
- sudo make install
-
-Usage
------
-
-Interface Creation
-~~~~~~~~~~~~~~~~~~
-
-Interfaces are dynamically created with following CLI:
-
-::
-
- create interface marvell pp2 name eth0
- set interface state mv-ppio-0/0 up
-
-Where ``eth0`` is linux interface name and ``mv-ppio-X/Y`` is VPP
-interface name where X is PP2 device ID and Y is PPIO ID Interface needs
-to be assigned to MUSDK in FDT configuration and linux interface state
-must be up.
-
-Interface Deletion
-~~~~~~~~~~~~~~~~~~
-
-Interface can be deleted with following CLI:
-
-::
-
- delete interface marvell pp2 <interface name>
-
-Interface Statistics
-~~~~~~~~~~~~~~~~~~~~
-
-Interface statistics can be displayed with
-``sh hardware-interface mv-ppio0/0`` command.
-
-Interaction with DPDK plugin
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This plugin doesn’t have any dependency on DPDK or DPDK plugin but it
-can work with DPDK plugin enabled or disabled. It is observed that
-performance is better around 30% when DPDK plugin is disabled, as DPDK
-plugin registers own buffer manager, which needs to deal with additional
-metadata in each packet.
-
-DPKD plugin can be disabled by adding following config to the
-startup.conf.
-
-::
-
- plugins {
- dpdk_plugin.so { disable }
- }
diff --git a/src/plugins/marvell/pp2/cli.c b/src/plugins/marvell/pp2/cli.c
deleted file mode 100644
index f4ecb1873c9..00000000000
--- a/src/plugins/marvell/pp2/cli.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <inttypes.h>
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-
-#include <marvell/pp2/pp2.h>
-
-static clib_error_t *
-mrvl_pp2_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
- vlib_cli_command_t * cmd)
-{
- unformat_input_t _line_input, *line_input = &_line_input;
- mrvl_pp2_create_if_args_t args = { 0 };
- uint val;
-
- /* Get a line of input. */
- if (!unformat_user (input, unformat_line_input, line_input))
- return 0;
-
- while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (line_input, "name %s", &args.name))
- ;
- else if (unformat (line_input, "rx-queue-size %u", &val))
- args.rx_q_sz = val;
- else if (unformat (line_input, "tx-queue-size %u", &val))
- args.tx_q_sz = val;
- else
- return clib_error_return (0, "unknown input `%U'",
- format_unformat_error, input);
- }
- unformat_free (line_input);
-
-
- mrvl_pp2_create_if (&args);
-
- vec_free (args.name);
-
- return args.error;
-}
-
-VLIB_CLI_COMMAND (mrvl_pp2_create_command, static) = {
- .path = "create interface marvell pp2",
- .short_help = "create interface marvell pp2 [name <ifname>] [rx-queue-size slots] [tx-queue-size slots]",
- .function = mrvl_pp2_create_command_fn,
-};
-
-static clib_error_t *
-mrvl_pp2_delete_command_fn (vlib_main_t * vm, unformat_input_t * input,
- vlib_cli_command_t * cmd)
-{
- unformat_input_t _line_input, *line_input = &_line_input;
- u32 sw_if_index = ~0;
- vnet_hw_interface_t *hw;
- mrvl_pp2_main_t *mm = &mrvl_pp2_main;
- mrvl_pp2_if_t *dif;
- vnet_main_t *vnm = vnet_get_main ();
-
- /* Get a line of input. */
- if (!unformat_user (input, unformat_line_input, line_input))
- return 0;
-
- while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (line_input, "sw_if_index %d", &sw_if_index))
- ;
- else if (unformat (line_input, "%U", unformat_vnet_sw_interface,
- vnm, &sw_if_index))
- ;
- else
- return clib_error_return (0, "unknown input `%U'",
- format_unformat_error, input);
- }
- unformat_free (line_input);
-
- if (sw_if_index == ~0)
- return clib_error_return (0,
- "please specify interface name or sw_if_index");
-
- hw = vnet_get_sup_hw_interface_api_visible_or_null (vnm, sw_if_index);
- if (hw == NULL || mrvl_pp2_device_class.index != hw->dev_class_index)
- return clib_error_return (0, "not a Marvell PP2 interface");
-
- dif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
-
- mrvl_pp2_delete_if (dif);
-
- return 0;
-}
-
-VLIB_CLI_COMMAND (mrvl_pp2_delete_command, static) = {
- .path = "delete interface marvell pp2",
- .short_help = "delete interface marvell pp2 "
- "{<interface> | sw_if_index <sw_idx>}",
- .function = mrvl_pp2_delete_command_fn,
-};
-
-clib_error_t *
-mrvl_pp2_cli_init (vlib_main_t * vm)
-{
- /* initialize binary API */
- mrvl_pp2_plugin_api_hookup (vm);
-
- return 0;
-}
-
-VLIB_INIT_FUNCTION (mrvl_pp2_cli_init);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/format.c b/src/plugins/marvell/pp2/format.c
deleted file mode 100644
index 877010ea561..00000000000
--- a/src/plugins/marvell/pp2/format.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/plugin/plugin.h>
-#include <marvell/pp2/pp2.h>
-
-static inline u32
-mrvl_get_u32_bits (void *start, int offset, int first, int last)
-{
- u32 value = *(u32 *) (((u8 *) start) + offset);
- if ((last == 0) && (first == 31))
- return value;
- value >>= last;
- value &= (1 << (first - last + 1)) - 1;
- return value;
-}
-
-u8 *
-format_mrvl_pp2_interface_name (u8 * s, va_list * args)
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- u32 dev_instance = va_arg (*args, u32);
- mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, dev_instance);
- return format (s, "mv-ppio-%d/%d", ppif->ppio->pp2_id, ppif->ppio->port_id);
-}
-
-#define foreach_ppio_statistics_entry \
- _(rx_packets) \
- _(rx_fullq_dropped) \
- _(rx_bm_dropped) \
- _(rx_early_dropped) \
- _(rx_fifo_dropped) \
- _(rx_cls_dropped) \
- _(tx_packets)
-
-#define foreach_ppio_inq_statistics_entry \
- _(enq_desc) \
- _(drop_early) \
- _(drop_fullq) \
- _(drop_bm)
-
-#define foreach_ppio_outq_statistics_entry \
- _(enq_desc) \
- _(enq_dec_to_ddr) \
- _(enq_buf_to_ddr) \
- _(deq_desc)
-
-u8 *
-format_mrvl_pp2_interface (u8 * s, va_list * args)
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- u32 dev_instance = va_arg (*args, u32);
- u32 indent = format_get_indent (s);
- mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, dev_instance);
- struct pp2_ppio_statistics stat;
- int i;
- u8 *s2 = 0;
-
- pp2_ppio_get_statistics (ppif->ppio, &stat, 0);
-
-#define _(c) if (stat.c) \
- s2 = format (s2, "\n%U%-25U%16Ld", \
- format_white_space, indent + 2, \
- format_c_identifier, #c, stat.c);
- foreach_ppio_statistics_entry;
-
- if (vec_len (s2))
- s = format (s, "Interface statistics:%v", s2);
- vec_reset_length (s2);
-
- vec_foreach_index (i, ppif->inqs)
- {
- struct pp2_ppio_inq_statistics stat = { 0 };
- pp2_ppio_inq_get_statistics (ppif->ppio, 0, i, &stat, 0);
-
- foreach_ppio_inq_statistics_entry;
-
- if (vec_len (s2))
- s = format (s, "\n%UInput queue %u statistics:%v",
- format_white_space, indent, i, s2);
- vec_reset_length (s2);
- }
- vec_foreach_index (i, ppif->outqs)
- {
- struct pp2_ppio_outq_statistics stat = { 0 };
-
- pp2_ppio_outq_get_statistics (ppif->ppio, i, &stat, 0);
-
- foreach_ppio_outq_statistics_entry;
-
- if (vec_len (s2))
- s = format (s, "\n%UOutput queue %u statistics:%v",
- format_white_space, indent, i, s2);
- vec_reset_length (s2);
- }
-#undef _
- vec_free (s2);
- return s;
-}
-
-#define foreach_pp2_rx_desc_field \
- _(0x00, 6, 0, l3_offset) \
- _(0x00, 12, 8, ip_hdlen) \
- _(0x00, 14, 13, ec) \
- _(0x00, 15, 15, es) \
- _(0x00, 19, 16, pool_id) \
- _(0x00, 21, 21, hwf_sync) \
- _(0x00, 22, 22, l4_chk_ok) \
- _(0x00, 23, 23, ip_frg) \
- _(0x00, 24, 24, ipv4_hdr_err) \
- _(0x00, 27, 25, l4_info) \
- _(0x00, 30, 28, l3_info) \
- _(0x00, 31, 31, buf_header) \
- _(0x04, 5, 0, lookup_id) \
- _(0x04, 8, 6, cpu_code) \
- _(0x04, 9, 9, pppoe) \
- _(0x04, 11, 10, l3_cast_info) \
- _(0x04, 13, 12, l2_cast_info) \
- _(0x04, 15, 14, vlan_info) \
- _(0x04, 31, 16, byte_count) \
- _(0x08, 11, 0, gem_port_id) \
- _(0x08, 13, 12, color) \
- _(0x08, 14, 14, gop_sop_u) \
- _(0x08, 15, 15, key_hash_enable) \
- _(0x08, 31, 16, l4chk) \
- _(0x0c, 31, 0, timestamp) \
- _(0x10, 31, 0, buf_phys_ptr_lo) \
- _(0x14, 7, 0, buf_phys_ptr_hi) \
- _(0x14, 31, 8, key_hash) \
- _(0x18, 31, 0, buf_virt_ptr_lo) \
- _(0x1c, 7, 0, buf_virt_ptr_hi) \
- _(0x1c, 14, 8, buf_qset_no) \
- _(0x1c, 15, 15, buf_type) \
- _(0x1c, 21, 16, mod_dscp) \
- _(0x1c, 24, 22, mod_pri) \
- _(0x1c, 25, 25, mdscp) \
- _(0x1c, 26, 26, mpri) \
- _(0x1c, 27, 27, mgpid) \
- _(0x1c, 31, 29, port_num)
-
-u8 *
-format_mrvl_pp2_input_trace (u8 * s, va_list * args)
-{
- vlib_main_t *vm = va_arg (*args, vlib_main_t *);
- vlib_node_t *node = va_arg (*args, vlib_node_t *);
- mrvl_pp2_input_trace_t *t = va_arg (*args, mrvl_pp2_input_trace_t *);
- vnet_main_t *vnm = vnet_get_main ();
- vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, t->hw_if_index);
- u32 indent = format_get_indent (s);
- struct pp2_ppio_desc *d = &t->desc;
- u32 r32;
-
- s = format (s, "pp2: %v (%d) next-node %U",
- hi->name, t->hw_if_index, format_vlib_next_node_name, vm,
- node->index, t->next_index);
- s = format (s, "\n%U", format_white_space, indent + 2);
-
-#define _(a, b, c, n) \
- r32 = mrvl_get_u32_bits (d, a, b, c); \
- if (r32 > 9) \
- s = format (s, "%s %u (0x%x)", #n, r32, r32); \
- else \
- s = format (s, "%s %u", #n,r32); \
- if (format_get_indent (s) > 72) \
- s = format (s, "\n%U", format_white_space, indent + 2); \
- else s = format (s, " ");
-
- foreach_pp2_rx_desc_field;
-#undef _
- return s;
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/input.c b/src/plugins/marvell/pp2/input.c
deleted file mode 100644
index 2545f91becb..00000000000
--- a/src/plugins/marvell/pp2/input.c
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#define _GNU_SOURCE
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-#include <vnet/devices/devices.h>
-#include <vnet/interface/rx_queue_funcs.h>
-
-#include <marvell/pp2/pp2.h>
-
-#define foreach_mrvl_pp2_input_error \
- _(PPIO_RECV, "pp2_ppio_recv error") \
- _(BPOOL_GET_NUM_BUFFS, "pp2_bpool_get_num_buffs error") \
- _(BPOOL_PUT_BUFFS, "pp2_bpool_put_buffs error") \
- _(BUFFER_ALLOC, "buffer alloc error") \
- _(MAC_CE, "MAC error (CRC error)") \
- _(MAC_OR, "overrun error") \
- _(MAC_RSVD, "unknown MAC error") \
- _(MAC_RE, "resource error") \
- _(IP_HDR, "ip4 header error")
-
-typedef enum
-{
-#define _(f,s) MRVL_PP2_INPUT_ERROR_##f,
- foreach_mrvl_pp2_input_error
-#undef _
- MRVL_PP2_INPUT_N_ERROR,
-} mrvl_pp2_input_error_t;
-
-static __clib_unused char *mrvl_pp2_input_error_strings[] = {
-#define _(n,s) s,
- foreach_mrvl_pp2_input_error
-#undef _
-};
-
-static_always_inline void
-mrvl_pp2_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node, u32 next0,
- vlib_buffer_t * b0, uword * n_trace,
- mrvl_pp2_if_t * ppif, struct pp2_ppio_desc *d)
-{
- if (PREDICT_TRUE (
- vlib_trace_buffer (vm, node, next0, b0, /* follow_chain */ 0)))
- {
- mrvl_pp2_input_trace_t *tr;
- vlib_set_trace_count (vm, node, --(*n_trace));
- tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
- tr->next_index = next0;
- tr->hw_if_index = ppif->hw_if_index;
- clib_memcpy_fast (&tr->desc, d, sizeof (struct pp2_ppio_desc));
- }
-}
-
-static_always_inline u16
-mrvl_pp2_set_buf_data_len_flags (vlib_buffer_t * b, struct pp2_ppio_desc *d,
- u32 add_flags)
-{
- u16 len;
- len = pp2_ppio_inq_desc_get_pkt_len (d);
- b->total_length_not_including_first_buffer = 0;
- b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | add_flags;
-
- if (add_flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
- vnet_buffer (b)->l2_hdr_offset = 2;
-
- if (add_flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID)
- {
- u16 offset = DM_RXD_GET_L3_OFF (d);
- vnet_buffer (b)->l3_hdr_offset = offset;
- b->current_data = offset;
- b->current_length = len - offset + 2;
- }
- else
- {
- b->current_data = 2;
- b->current_length = len;
- }
-
- if (add_flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID)
- vnet_buffer (b)->l4_hdr_offset = vnet_buffer (b)->l3_hdr_offset +
- DM_RXD_GET_IPHDR_LEN (d) * 4;
-
- return len;
-}
-
-static_always_inline u16
-mrvl_pp2_next_from_desc (vlib_node_runtime_t * node, struct pp2_ppio_desc * d,
- vlib_buffer_t * b, u32 * next)
-{
- u8 l3_info;
- /* ES bit set means MAC error - drop and count */
- if (PREDICT_FALSE (DM_RXD_GET_ES (d)))
- {
- *next = VNET_DEVICE_INPUT_NEXT_DROP;
- u8 ec = DM_RXD_GET_EC (d);
- if (ec == 0)
- b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_CE];
- else if (ec == 1)
- b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_OR];
- else if (ec == 2)
- b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_RSVD];
- else if (ec == 3)
- b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_RE];
- return mrvl_pp2_set_buf_data_len_flags (b, d, 0);
- }
- l3_info = DM_RXD_GET_L3_PRS_INFO (d);
-
- /* ipv4 packet can be value 1, 2 or 3 */
- if (PREDICT_TRUE ((l3_info - 1) < 3))
- {
- if (PREDICT_FALSE (DM_RXD_GET_L3_IP4_HDR_ERR (d) != 0))
- {
- *next = VNET_DEVICE_INPUT_NEXT_DROP;
- b->error = node->errors[MRVL_PP2_INPUT_ERROR_IP_HDR];
- return mrvl_pp2_set_buf_data_len_flags (b, d, 0);
- }
- *next = VNET_DEVICE_INPUT_NEXT_IP4_NCS_INPUT;
- return mrvl_pp2_set_buf_data_len_flags
- (b, d,
- VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
- VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
- VNET_BUFFER_F_L4_HDR_OFFSET_VALID | VNET_BUFFER_F_IS_IP4);
- }
-
- /* ipv4 packet can be value 4 or 5 */
- if (PREDICT_TRUE ((l3_info - 4) < 2))
- {
- *next = VNET_DEVICE_INPUT_NEXT_IP6_INPUT;
- return mrvl_pp2_set_buf_data_len_flags
- (b, d,
- VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
- VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
- VNET_BUFFER_F_L4_HDR_OFFSET_VALID | VNET_BUFFER_F_IS_IP6);
- }
-
- *next = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
- return mrvl_pp2_set_buf_data_len_flags (b, d,
- VNET_BUFFER_F_L2_HDR_OFFSET_VALID);
-}
-
-static_always_inline uword
-mrvl_pp2_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame, mrvl_pp2_if_t * ppif,
- u16 qid)
-{
- vnet_main_t *vnm = vnet_get_main ();
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- u32 thread_index = vm->thread_index;
- mrvl_pp2_inq_t *inq = vec_elt_at_index (ppif->inqs, qid);
- uword n_trace = vlib_get_trace_count (vm, node);
- mrvl_pp2_per_thread_data_t *ptd =
- vec_elt_at_index (ppm->per_thread_data, thread_index);
- u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
- u32 sw_if_index[VLIB_N_RX_TX];
- u32 n_rx_packets = 0;
- u32 n_rx_bytes = 0;
- u32 *to_next = 0;
- struct pp2_ppio_desc *d;
- u16 n_desc = VLIB_FRAME_SIZE;
- u32 n_bufs;
- u32 *buffers;
- int i;
-
- vec_validate_aligned (ptd->descs, n_desc, CLIB_CACHE_LINE_BYTES);
- if (PREDICT_FALSE (pp2_ppio_recv (ppif->ppio, 0, qid, ptd->descs, &n_desc)))
- {
- vlib_error_count (vm, node->node_index, MRVL_PP2_INPUT_ERROR_PPIO_RECV,
- 1);
- n_desc = 0;
- }
- n_rx_packets = n_desc;
-
- for (i = 0; i < n_desc; i++)
- ptd->buffers[i] = pp2_ppio_inq_desc_get_cookie (&ptd->descs[i]);
-
- d = ptd->descs;
- buffers = ptd->buffers;
- sw_if_index[VLIB_RX] = ppif->sw_if_index;
- sw_if_index[VLIB_TX] = (u32) ~ 0;
- while (n_desc)
- {
- u32 n_left_to_next;
- vlib_buffer_t *b0, *b1;
- u32 bi0, bi1;
- u32 next0, next1;
- vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
- while (n_desc >= 4 && n_left_to_next >= 2)
- {
- /* prefetch */
- bi0 = buffers[0];
- bi1 = buffers[1];
- to_next[0] = bi0;
- to_next[1] = bi1;
- b0 = vlib_get_buffer (vm, bi0);
- b1 = vlib_get_buffer (vm, bi1);
-
- if (PREDICT_TRUE (ppif->per_interface_next_index == ~0))
- {
- n_rx_bytes += mrvl_pp2_next_from_desc (node, d, b0, &next0);
- n_rx_bytes += mrvl_pp2_next_from_desc (node, d + 1, b1, &next1);
- vnet_feature_start_device_input (ppif->sw_if_index, &next0, b0);
- vnet_feature_start_device_input (ppif->sw_if_index, &next1, b1);
- }
- else
- {
- n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b0, d, 0);
- n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b1, d + 1, 0);
- next0 = next1 = ppif->per_interface_next_index;
- }
-
- clib_memcpy_fast (vnet_buffer (b0)->sw_if_index, sw_if_index,
- sizeof (sw_if_index));
- clib_memcpy_fast (vnet_buffer (b1)->sw_if_index, sw_if_index,
- sizeof (sw_if_index));
-
- if (PREDICT_FALSE (n_trace > 0))
- {
- mrvl_pp2_input_trace (vm, node, next0, b0, &n_trace, ppif, d);
- if (n_trace > 0)
- mrvl_pp2_input_trace (vm, node, next1, b1, &n_trace, ppif,
- d + 1);
- }
-
- to_next += 2;
- n_left_to_next -= 2;
- d += 2;
- buffers += 2;
- n_desc -= 2;
-
- /* enqueue */
- vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
- n_left_to_next, bi0, bi1, next0,
- next1);
-
- }
- while (n_desc && n_left_to_next)
- {
- u32 bi0 = buffers[0];
- to_next[0] = bi0;
- b0 = vlib_get_buffer (vm, bi0);
-
- if (PREDICT_TRUE (ppif->per_interface_next_index == ~0))
- {
- n_rx_bytes += mrvl_pp2_next_from_desc (node, d, b0, &next0);
- vnet_feature_start_device_input (ppif->sw_if_index, &next0, b0);
- }
- else
- {
- n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b0, d, 0);
- next0 = ppif->per_interface_next_index;
- }
-
- clib_memcpy_fast (vnet_buffer (b0)->sw_if_index, sw_if_index,
- sizeof (sw_if_index));
-
- if (PREDICT_FALSE (n_trace > 0))
- mrvl_pp2_input_trace (vm, node, next0, b0, &n_trace, ppif, d);
-
- to_next += 1;
- n_left_to_next--;
- d++;
- buffers++;
- n_desc--;
-
- /* enqueue */
- vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
- n_left_to_next, bi0, next0);
- }
- vlib_put_next_frame (vm, node, next_index, n_left_to_next);
- }
- vlib_increment_combined_counter (vnm->
- interface_main.combined_sw_if_counters +
- VNET_INTERFACE_COUNTER_RX, thread_index,
- ppif->hw_if_index, n_rx_packets,
- n_rx_bytes);
-
- if (PREDICT_FALSE (pp2_bpool_get_num_buffs (inq->bpool, &n_bufs)))
- {
- vlib_error_count (vm, node->node_index,
- MRVL_PP2_INPUT_ERROR_BPOOL_GET_NUM_BUFFS, 1);
- goto done;
- }
-
- n_bufs = inq->size - n_bufs;
- while (n_bufs >= MRVL_PP2_BUFF_BATCH_SZ)
- {
- u16 n_alloc, i;
- struct buff_release_entry *e = ptd->bre;
- u32 *buffers = ptd->buffers;
-
- n_alloc = vlib_buffer_alloc (vm, ptd->buffers, MRVL_PP2_BUFF_BATCH_SZ);
- i = n_alloc;
-
- if (PREDICT_FALSE (n_alloc == 0))
- {
- vlib_error_count (vm, node->node_index,
- MRVL_PP2_INPUT_ERROR_BUFFER_ALLOC, 1);
- goto done;
- }
-
- while (i--)
- {
- u32 bi = buffers[0];
- vlib_buffer_t *b = vlib_get_buffer (vm, bi);
- e->buff.addr = vlib_buffer_get_pa (vm, b) - 64;
- e->buff.cookie = bi;
- e->bpool = inq->bpool;
- e++;
- buffers++;
- }
-
- i = n_alloc;
- if (PREDICT_FALSE (pp2_bpool_put_buffs (ptd->hif, ptd->bre, &i)))
- {
- vlib_error_count (vm, node->node_index,
- MRVL_PP2_INPUT_ERROR_BPOOL_PUT_BUFFS, 1);
- vlib_buffer_free (vm, ptd->buffers, n_alloc);
- goto done;
- }
-
- if (PREDICT_FALSE (i != n_alloc))
- vlib_buffer_free (vm, ptd->buffers + i, n_alloc - i);
-
- n_bufs -= i;
- }
-
-done:
- return n_rx_packets;
-}
-
-uword
-mrvl_pp2_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame)
-{
- u32 n_rx = 0;
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- vnet_hw_if_rxq_poll_vector_t *pv;
-
- pv = vnet_hw_if_get_rxq_poll_vector (vm, node);
-
- for (int i = 0; i < vec_len (pv); i++)
- {
- mrvl_pp2_if_t *ppif;
- ppif = vec_elt_at_index (ppm->interfaces, pv[i].dev_instance);
- if (ppif->flags & MRVL_PP2_IF_F_ADMIN_UP)
- n_rx +=
- mrvl_pp2_device_input_inline (vm, node, frame, ppif, pv[i].queue_id);
- }
- return n_rx;
-}
-
-VLIB_REGISTER_NODE (mrvl_pp2_input_node) = {
- .function = mrvl_pp2_input_fn,
- .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
- .name = "mrvl-pp2-input",
- .sibling_of = "device-input",
- .format_trace = format_mrvl_pp2_input_trace,
- .type = VLIB_NODE_TYPE_INPUT,
- .state = VLIB_NODE_STATE_POLLING,
- .n_errors = MRVL_PP2_INPUT_N_ERROR,
- .error_strings = mrvl_pp2_input_error_strings,
-};
-
-
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/output.c b/src/plugins/marvell/pp2/output.c
deleted file mode 100644
index 911b2f55a17..00000000000
--- a/src/plugins/marvell/pp2/output.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-#include <vnet/devices/devices.h>
-
-#include <marvell/pp2/pp2.h>
-
-uword
-mrvl_pp2_interface_tx (vlib_main_t * vm,
- vlib_node_runtime_t * node, vlib_frame_t * frame)
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
- mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, rd->dev_instance);
- u32 thread_index = vm->thread_index;
- mrvl_pp2_per_thread_data_t *ptd =
- vec_elt_at_index (ppm->per_thread_data, thread_index);
- u8 qid = thread_index;
- mrvl_pp2_outq_t *outq = vec_elt_at_index (ppif->outqs, qid);
- u32 *buffers = vlib_frame_vector_args (frame);
- u16 n_desc = frame->n_vectors, n_left = n_desc, n_sent = n_desc, n_done;
- struct pp2_ppio_desc *d;
- u16 mask = outq->size - 1;
-
- if (PREDICT_FALSE (pp2_ppio_get_num_outq_done (ppif->ppio, ptd->hif, qid,
- &n_done)))
- {
- n_done = 0;
- vlib_error_count (vm, node->node_index,
- MRVL_PP2_TX_ERROR_PPIO_GET_NUM_OUTQ_DONE, 1);
- }
-
- if (n_done)
- {
- u16 n_free = clib_min (n_done, outq->size - (outq->tail & mask));
- vlib_buffer_free (vm, outq->buffers + (outq->tail & mask), n_free);
- if (PREDICT_FALSE (n_free < n_done))
- vlib_buffer_free (vm, outq->buffers, n_done - n_free);
- outq->tail += n_done;
- }
-
- vec_validate_aligned (ptd->descs, n_left, CLIB_CACHE_LINE_BYTES);
- d = ptd->descs;
- while (n_left)
- {
- u32 bi0 = buffers[0];
- vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
- u64 paddr = vlib_buffer_get_pa (vm, b0);
-
- pp2_ppio_outq_desc_reset (d);
- pp2_ppio_outq_desc_set_phys_addr (d, paddr + b0->current_data);
- pp2_ppio_outq_desc_set_pkt_offset (d, 0);
- pp2_ppio_outq_desc_set_pkt_len (d, b0->current_length);
- d++;
- buffers++;
- n_left--;
- }
-
- if (pp2_ppio_send (ppif->ppio, ptd->hif, qid, ptd->descs, &n_sent))
- {
- n_sent = 0;
- vlib_error_count (vm, node->node_index, MRVL_PP2_TX_ERROR_PPIO_SEND, 1);
- }
-
- /* free unsent buffers */
- if (PREDICT_FALSE (n_sent != n_desc))
- {
- vlib_buffer_free (vm, vlib_frame_vector_args (frame) + n_sent,
- frame->n_vectors - n_sent);
- vlib_error_count (vm, node->node_index, MRVL_PP2_TX_ERROR_NO_FREE_SLOTS,
- frame->n_vectors - n_sent);
- }
-
- /* store buffer index for each enqueued packet into the ring
- so we can know what to free after packet is sent */
- if (n_sent)
- {
- u16 slot = outq->head & mask;
- buffers = vlib_frame_vector_args (frame);
- u16 n_copy = clib_min (outq->size - slot, n_sent);
-
- vlib_buffer_copy_indices (outq->buffers + slot, buffers, n_copy);
- if (PREDICT_FALSE (n_copy < n_sent))
- clib_memcpy_fast (outq->buffers, buffers + n_copy,
- (n_sent - n_copy) * sizeof (u32));
-
- outq->head += n_sent;
- }
-
- return n_sent;
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/pp2.c b/src/plugins/marvell/pp2/pp2.c
deleted file mode 100644
index 030ab9b4496..00000000000
--- a/src/plugins/marvell/pp2/pp2.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/plugin/plugin.h>
-#include <marvell/pp2/pp2.h>
-#include <vnet/interface/rx_queue_funcs.h>
-
-/* size of DMA memory used by musdk (not used for buffers) */
-#define MV_SYS_DMA_MEM_SZ (2 << 20)
-/* number of HIFs reserved (first X) */
-#define NUM_HIFS_RSVD 4
-/* number of buffer pools reserved (first X) */
-#define NUM_BPOOLS_RSVD 7
-
-mrvl_pp2_main_t mrvl_pp2_main;
-extern vnet_device_class_t ppa2_device_class;
-
-static void
-mrvl_pp2_main_deinit ()
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- int i;
- vec_foreach_index (i, ppm->per_thread_data)
- {
- mrvl_pp2_per_thread_data_t *ptd = vec_elt_at_index (ppm->per_thread_data,
- i);
- if (ptd->hif)
- pp2_hif_deinit (ptd->hif);
- vec_free (ptd->descs);
- }
- vec_free (ppm->per_thread_data);
- pp2_deinit ();
- mv_sys_dma_mem_destroy ();
-}
-
-static clib_error_t *
-mrvl_pp2_main_init ()
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- vlib_thread_main_t *tm = vlib_get_thread_main ();
- clib_error_t *err = 0;
- struct pp2_init_params init_params = { 0 };
- int i, rv;
- u8 *s = 0;
-
- rv = mv_sys_dma_mem_init (MV_SYS_DMA_MEM_SZ);
- if (rv)
- return clib_error_return (0, "mv_sys_dma_mem_init failed, rv = %u", rv);
-
- init_params.hif_reserved_map = ((1 << NUM_HIFS_RSVD) - 1);
- init_params.bm_pool_reserved_map = ((1 << NUM_BPOOLS_RSVD) - 1);
- rv = pp2_init (&init_params);
- if (rv)
- {
- err = clib_error_return (0, "mrvl_pp2_init failed, rv = %u", rv);
- goto done;
- }
-
- vec_validate_aligned (ppm->per_thread_data, tm->n_vlib_mains - 1,
- CLIB_CACHE_LINE_BYTES);
-
- vec_foreach_index (i, ppm->per_thread_data)
- {
- mrvl_pp2_per_thread_data_t *ptd = vec_elt_at_index (ppm->per_thread_data,
- i);
- struct pp2_hif_params hif_params = { 0 };
- vec_reset_length (s);
- s = format (s, "hif-%d%c", NUM_HIFS_RSVD + i, 0);
- hif_params.match = (char *) s;
- hif_params.out_size = 2048; /* FIXME */
- if (pp2_hif_init (&hif_params, &ptd->hif))
- {
- err = clib_error_return (0, "hif '%s' init failed", s);
- goto done;
- }
- }
-
-done:
- if (err)
- mrvl_pp2_main_deinit ();
- vec_free (s);
- return err;
-}
-
-static u32
-mrvl_pp2_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi,
- u32 flags)
-{
- /* nothing for now */
- return 0;
-}
-
-void
-mrvl_pp2_delete_if (mrvl_pp2_if_t * ppif)
-{
- vlib_main_t *vm = vlib_get_main ();
- vnet_main_t *vnm = vnet_get_main ();
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- mrvl_pp2_outq_t *outq;
- mrvl_pp2_inq_t *inq;
-
- if (ppif->hw_if_index != ~0)
- ethernet_delete_interface (vnm, ppif->hw_if_index);
-
- if (ppif->ppio)
- {
- pp2_ppio_disable (ppif->ppio);
- pp2_ppio_deinit (ppif->ppio);
- }
-
- /* free buffers hanging in the tx ring */
- vec_foreach (outq, ppif->outqs)
- {
- while (outq->tail < outq->head)
- {
- u16 slot = outq->tail & (outq->size - 1);
- vlib_buffer_free (vm, outq->buffers + slot, 1);
- outq->tail++;
- }
- vec_free (outq->buffers);
- }
- vec_free (ppif->outqs);
-
- /* free buffers hangin in the rx buffer pool */
- vec_foreach (inq, ppif->inqs)
- if (inq->bpool)
- {
- u32 n_bufs = 0;
- pp2_bpool_get_num_buffs (inq->bpool, &n_bufs);
- while (n_bufs--)
- {
- struct pp2_buff_inf binf;
- if (pp2_bpool_get_buff (ppm->per_thread_data[0].hif, inq->bpool,
- &binf) == 0)
- {
- u32 bi = binf.cookie;
- vlib_buffer_free (vm, &bi, 1);
- }
- }
- pp2_bpool_deinit (inq->bpool);
- }
- vec_free (ppif->inqs);
-
-
- pool_put (ppm->interfaces, ppif);
-
- if (pool_elts (ppm->interfaces) == 0)
- mrvl_pp2_main_deinit ();
-}
-
-void
-mrvl_pp2_create_if (mrvl_pp2_create_if_args_t * args)
-{
- vlib_main_t *vm = vlib_get_main ();
- vnet_main_t *vnm = vnet_get_main ();
- vlib_thread_main_t *tm = vlib_get_thread_main ();
- vnet_eth_interface_registration_t eir = {};
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- struct pp2_bpool_params bpool_params = { 0 };
- struct pp2_ppio_params ppio_params = { 0 };
- struct pp2_ppio_inq_params inq_params = { 0 };
- vnet_sw_interface_t *sw;
- mrvl_pp2_if_t *ppif = 0;
- u8 pp2_id, port_id, *s = 0;
- eth_addr_t mac_addr;
- u8 n_outqs, n_inqs = 1;
- int i;
-
- if (tm->n_vlib_mains > PP2_PPIO_MAX_NUM_OUTQS)
- {
- args->rv = VNET_API_ERROR_INIT_FAILED;
- args->error = clib_error_return (0, "number of threads (main + workers)"
- " is bigger than number of output "
- "queues (%u)", PP2_PPIO_MAX_NUM_OUTQS);
- return;
- }
- n_outqs = tm->n_vlib_mains;
-
- /* defaults */
- args->tx_q_sz = args->tx_q_sz ? args->tx_q_sz : 2 * VLIB_FRAME_SIZE;
- args->rx_q_sz = args->rx_q_sz ? args->rx_q_sz : 2 * VLIB_FRAME_SIZE;
-
- if (vec_len (ppm->per_thread_data) == 0)
- {
- if ((args->error = mrvl_pp2_main_init ()) != 0)
- {
- args->rv = VNET_API_ERROR_INIT_FAILED;
- return;
- }
- }
-
- pool_get_zero (ppm->interfaces, ppif);
- ppif->dev_instance = ppif - ppm->interfaces;
- ppif->hw_if_index = ~0;
- vec_validate_aligned (ppif->inqs, n_inqs - 1, CLIB_CACHE_LINE_BYTES);
- vec_validate_aligned (ppif->outqs, n_outqs - 1, CLIB_CACHE_LINE_BYTES);
-
- for (i = 0; i < n_inqs; i++)
- {
- mrvl_pp2_inq_t *inq = vec_elt_at_index (ppif->inqs, i);
- inq->size = args->rx_q_sz;
- }
- for (i = 0; i < n_outqs; i++)
- {
- mrvl_pp2_outq_t *outq = vec_elt_at_index (ppif->outqs, i);
- outq->size = args->tx_q_sz;
- vec_validate_aligned (outq->buffers, outq->size, CLIB_CACHE_LINE_BYTES);
- }
-
- if (pp2_netdev_get_ppio_info ((char *) args->name, &pp2_id, &port_id))
- {
- args->rv = VNET_API_ERROR_INVALID_INTERFACE;
- args->error = clib_error_return (0, "Invalid interface '%s'",
- args->name);
- goto error;
- }
-
- /* FIXME bpool bit select per pp */
- s = format (s, "pool-%d:%d%c", pp2_id, pp2_id + 8, 0);
- bpool_params.match = (char *) s;
- bpool_params.buff_len = vlib_buffer_get_default_data_size (vm);
- /* FIXME +64 ? */
- if (pp2_bpool_init (&bpool_params, &ppif->inqs[0].bpool))
- {
- args->rv = VNET_API_ERROR_INIT_FAILED;
- args->error = clib_error_return (0, "bpool '%s' init failed", s);
- goto error;
- }
- vec_reset_length (s);
-
- s = format (s, "ppio-%d:%d%c", pp2_id, port_id, 0);
- ppio_params.match = (char *) s;
- ppio_params.type = PP2_PPIO_T_NIC;
- inq_params.size = args->rx_q_sz;
- ppio_params.inqs_params.num_tcs = 1;
- ppio_params.inqs_params.tcs_params[0].pkt_offset = 0;
- ppio_params.inqs_params.tcs_params[0].num_in_qs = n_inqs;
- ppio_params.inqs_params.tcs_params[0].inqs_params = &inq_params;
- ppio_params.inqs_params.tcs_params[0].pools[0][0] = ppif->inqs[0].bpool;
- ppio_params.outqs_params.num_outqs = n_outqs;
- for (i = 0; i < n_outqs; i++)
- {
- ppio_params.outqs_params.outqs_params[i].weight = 1;
- ppio_params.outqs_params.outqs_params[i].size = args->tx_q_sz;
- }
- if (pp2_ppio_init (&ppio_params, &ppif->ppio))
- {
- args->rv = VNET_API_ERROR_INIT_FAILED;
- args->error = clib_error_return (0, "ppio '%s' init failed", s);
- goto error;
- }
- vec_reset_length (s);
-
- if (pp2_ppio_get_mac_addr (ppif->ppio, mac_addr))
- {
- args->rv = VNET_API_ERROR_INIT_FAILED;
- args->error =
- clib_error_return (0, "%s: pp2_ppio_get_mac_addr failed", s);
- goto error;
- }
-
- eir.dev_class_index = mrvl_pp2_device_class.index;
- eir.dev_instance = ppif->dev_instance;
- eir.address = mac_addr;
- eir.cb.flag_change = mrvl_pp2_eth_flag_change;
- ppif->hw_if_index = vnet_eth_register_interface (vnm, &eir);
-
- sw = vnet_get_hw_sw_interface (vnm, ppif->hw_if_index);
- ppif->sw_if_index = sw->sw_if_index;
- ppif->per_interface_next_index = ~0;
- args->sw_if_index = sw->sw_if_index;
- vnet_hw_if_set_input_node (vnm, ppif->hw_if_index,
- mrvl_pp2_input_node.index);
- /* FIXME: only one RX queue ? */
- ppif->inqs[0].queue_index = vnet_hw_if_register_rx_queue (
- vnm, ppif->hw_if_index, 0, VNET_HW_IF_RXQ_THREAD_ANY);
-
- vnet_hw_if_set_rx_queue_mode (vnm, ppif->inqs[0].queue_index,
- VNET_HW_IF_RX_MODE_POLLING);
- vnet_hw_if_update_runtime_data (vnm, ppif->hw_if_index);
- vnet_hw_interface_set_flags (vnm, ppif->hw_if_index,
- VNET_HW_INTERFACE_FLAG_LINK_UP);
- goto done;
-
-error:
- mrvl_pp2_delete_if (ppif);
-done:
- vec_free (s);
-}
-
-static clib_error_t *
-mrvl_pp2_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
- u32 flags)
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
- mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, hw->dev_instance);
- static clib_error_t *error = 0;
- int is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
- int rv;
-
- if (is_up)
- rv = pp2_ppio_enable (ppif->ppio);
- else
- rv = pp2_ppio_disable (ppif->ppio);
-
- if (rv)
- return clib_error_return (0, "failed to %s interface",
- is_up ? "enable" : "disable");
-
- if (is_up)
- ppif->flags |= MRVL_PP2_IF_F_ADMIN_UP;
- else
- ppif->flags &= ~MRVL_PP2_IF_F_ADMIN_UP;
-
- return error;
-}
-
-static void
-mrvl_pp2_clear_interface_counters (u32 instance)
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, instance);
- struct pp2_ppio_statistics stats;
-
- pp2_ppio_get_statistics (ppif->ppio, &stats, 1);
-}
-
-static void
-mrvl_pp2_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
- u32 node_index)
-{
- mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
- vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
- mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, hw->dev_instance);
-
- /* Shut off redirection */
- if (node_index == ~0)
- {
- ppif->per_interface_next_index = node_index;
- return;
- }
-
- ppif->per_interface_next_index =
- vlib_node_add_next (vlib_get_main (), mrvl_pp2_input_node.index,
- node_index);
-}
-
-static char *mrvl_pp2_tx_func_error_strings[] = {
-#define _(n,s) s,
- foreach_mrvl_pp2_tx_func_error
-#undef _
-};
-
-VNET_DEVICE_CLASS (mrvl_pp2_device_class,) =
-{
- .name = "Marvell PPv2 interface",
- .format_device_name = format_mrvl_pp2_interface_name,
- .format_device = format_mrvl_pp2_interface,
- .tx_function = mrvl_pp2_interface_tx,
- .tx_function_n_errors = MRVL_PP2_TX_N_ERROR,
- .tx_function_error_strings = mrvl_pp2_tx_func_error_strings,
- .admin_up_down_function = mrvl_pp2_interface_admin_up_down,
- .clear_counters = mrvl_pp2_clear_interface_counters,
- .rx_redirect_to_node = mrvl_pp2_set_interface_next_node,
-};
-
-static clib_error_t *
-mrvl_pp2_init (vlib_main_t * vm)
-{
- return 0;
-}
-
-VLIB_INIT_FUNCTION (mrvl_pp2_init);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/pp2.h b/src/plugins/marvell/pp2/pp2.h
deleted file mode 100644
index abb8e573a37..00000000000
--- a/src/plugins/marvell/pp2/pp2.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#define MVCONF_DBG_LEVEL 0
-#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32
-#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64
-#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64
-#define MVCONF_SYS_DMA_UIO
-#define MVCONF_TYPES_PUBLIC
-#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC
-
-#include <vlib/vlib.h>
-
-#include "mv_std.h"
-#include "env/mv_sys_dma.h"
-#include "drivers/mv_pp2.h"
-#include <drivers/mv_pp2_bpool.h>
-#include <drivers/mv_pp2_ppio.h>
-
-typedef struct
-{
- CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- u16 size;
- u32 queue_index;
- struct pp2_bpool *bpool;
-} mrvl_pp2_inq_t;
-
-typedef struct
-{
- CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- u16 size;
- u32 *buffers;
- u16 head;
- u16 tail;
-} mrvl_pp2_outq_t;
-
-typedef struct
-{
- CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- u32 flags;
-#define MRVL_PP2_IF_F_ADMIN_UP (1 << 0)
- struct pp2_ppio *ppio;
- u32 per_interface_next_index;
-
- mrvl_pp2_inq_t *inqs;
- mrvl_pp2_outq_t *outqs;
-
- u32 dev_instance;
- u32 sw_if_index;
- u32 hw_if_index;
-} mrvl_pp2_if_t;
-
-#define MRVL_PP2_BUFF_BATCH_SZ VLIB_FRAME_SIZE
-
-typedef struct
-{
- CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- struct pp2_hif *hif;
- struct pp2_ppio_desc *descs;
- struct buff_release_entry bre[MRVL_PP2_BUFF_BATCH_SZ];
- u32 buffers[VLIB_FRAME_SIZE];
-} mrvl_pp2_per_thread_data_t;
-
-typedef struct
-{
- mrvl_pp2_if_t *interfaces;
- mrvl_pp2_per_thread_data_t *per_thread_data;
-
- /* API message ID base */
- u16 msg_id_base;
-} mrvl_pp2_main_t;
-
-extern vnet_device_class_t mrvl_pp2_device_class;
-extern mrvl_pp2_main_t mrvl_pp2_main;
-
-typedef struct
-{
- u8 *name;
- u16 rx_q_sz;
- u16 tx_q_sz;
-
- /* return */
- i32 rv;
- u32 sw_if_index;
- clib_error_t *error;
-} mrvl_pp2_create_if_args_t;
-
-void mrvl_pp2_create_if (mrvl_pp2_create_if_args_t * args);
-void mrvl_pp2_delete_if (mrvl_pp2_if_t * dfif);
-clib_error_t *mrvl_pp2_plugin_api_hookup (vlib_main_t * vm);
-
-/* output.c */
-
-#define foreach_mrvl_pp2_tx_func_error \
- _(NO_FREE_SLOTS, "no free tx slots") \
- _(PPIO_SEND, "pp2_ppio_send errors") \
- _(PPIO_GET_NUM_OUTQ_DONE, "pp2_ppio_get_num_outq_done errors")
-
-typedef enum
-{
-#define _(f,s) MRVL_PP2_TX_ERROR_##f,
- foreach_mrvl_pp2_tx_func_error
-#undef _
- MRVL_PP2_TX_N_ERROR,
-} mrvl_pp2_tx_func_error_t;
-
-uword mrvl_pp2_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame);
-
-/* input.c */
-
-typedef struct
-{
- u32 next_index;
- u32 hw_if_index;
- struct pp2_ppio_desc desc;
-} mrvl_pp2_input_trace_t;
-
-extern vlib_node_registration_t mrvl_pp2_input_node;
-
-/* format.c */
-format_function_t format_mrvl_pp2_input_trace;
-format_function_t format_mrvl_pp2_interface;
-format_function_t format_mrvl_pp2_interface_name;
-
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/pp2_api.c b/src/plugins/marvell/pp2/pp2_api.c
deleted file mode 100644
index c1f3a9e1d1d..00000000000
--- a/src/plugins/marvell/pp2/pp2_api.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2019 Arm Limited.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-
-#include <marvell/pp2/pp2.h>
-
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-
-/* define message IDs */
-#include <marvell/pp2/pp2.api_enum.h>
-#include <marvell/pp2/pp2.api_types.h>
-
-#define REPLY_MSG_ID_BASE (pp2->msg_id_base)
-#include <vlibapi/api_helper_macros.h>
-
-static void
-vl_api_mrvl_pp2_create_t_handler (vl_api_mrvl_pp2_create_t * mp)
-{
- mrvl_pp2_main_t *pp2 = &mrvl_pp2_main;
- mrvl_pp2_create_if_args_t args = { 0 };
- vl_api_mrvl_pp2_create_reply_t *rmp;
- int rv;
-
- args.name = format (0, "%s", mp->if_name);
- args.rx_q_sz = ntohs (mp->rx_q_sz);
- args.tx_q_sz = ntohs (mp->tx_q_sz);
- mrvl_pp2_create_if (&args);
- rv = args.rv;
- vec_free (args.name);
- if (args.error)
- {
- clib_error_free (args.error);
- }
- REPLY_MACRO2 (VL_API_MRVL_PP2_CREATE_REPLY,
- ({ rmp->sw_if_index = ntohl (args.sw_if_index); }));
-}
-
-static void
-vl_api_mrvl_pp2_delete_t_handler (vl_api_mrvl_pp2_delete_t * mp)
-{
- vnet_main_t *vnm = vnet_get_main ();
- vnet_hw_interface_t *hw;
- mrvl_pp2_main_t *pp2 = &mrvl_pp2_main;
- vl_api_mrvl_pp2_delete_reply_t *rmp;
- mrvl_pp2_if_t *dif;
- int rv = 0;
- mp->sw_if_index = ntohl (mp->sw_if_index);
- hw = vnet_get_sup_hw_interface (vnm, mp->sw_if_index);
- if (hw == NULL || mrvl_pp2_device_class.index != hw->dev_class_index)
- {
- rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
- goto reply;
- }
-
- dif = pool_elt_at_index (pp2->interfaces, hw->dev_instance);
-
- mrvl_pp2_delete_if (dif);
-
-reply:
- REPLY_MACRO (VL_API_MRVL_PP2_DELETE_REPLY);
-}
-
-#include <marvell/pp2/pp2.api.c>
-/* set up the API message handling tables */
-clib_error_t *
-mrvl_pp2_plugin_api_hookup (vlib_main_t * vm)
-{
- mrvl_pp2_main_t *pp2 = &mrvl_pp2_main;
-
- /* ask for a correctly-sized block of API message decode slots */
- pp2->msg_id_base = setup_message_id_table ();
-
- return 0;
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/marvell/pp2/pp2_test.c b/src/plugins/marvell/pp2/pp2_test.c
deleted file mode 100644
index 26a9e9a6e34..00000000000
--- a/src/plugins/marvell/pp2/pp2_test.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2019 Arm Limited.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-
-#include <vat/vat.h>
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-
-#include <vppinfra/error.h>
-#include <marvell/pp2/pp2.h>
-
-#define __plugin_msg_base pp2_test_main.msg_id_base
-#include <vlibapi/vat_helper_macros.h>
-
-/* declare message IDs */
-#include <marvell/pp2/pp2.api_enum.h>
-#include <marvell/pp2/pp2.api_types.h>
-
-typedef struct
-{
- /* API message ID base */
- u16 msg_id_base;
- vat_main_t *vat_main;
-} pp2_test_main_t;
-
-pp2_test_main_t pp2_test_main;
-
-/* mrvl_pp2 create API */
-static int
-api_mrvl_pp2_create (vat_main_t * vam)
-{
- unformat_input_t *i = vam->input;
- vl_api_mrvl_pp2_create_t *mp;
- mrvl_pp2_create_if_args_t args;
- int ret;
- u16 size;
-
- clib_memset (&args, 0, sizeof (mrvl_pp2_create_if_args_t));
- while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (i, "name %s", &args.name))
- ;
- else if (unformat (i, "rx-queue-size %u", &size))
- args.rx_q_sz = size;
- else if (unformat (i, "tx-queue-size %u", &size))
- args.tx_q_sz = size;
- else
- {
- clib_warning ("unknown input '%U'", format_unformat_error, i);
- return -99;
- }
- }
-
- M (MRVL_PP2_CREATE, mp);
-
- strncpy_s ((char *) mp->if_name, ARRAY_LEN (mp->if_name),
- (char *) (args.name), strlen ((char *) args.name));
- mp->rx_q_sz = clib_host_to_net_u16 (args.rx_q_sz);
- mp->tx_q_sz = clib_host_to_net_u16 (args.tx_q_sz);
-
- S (mp);
- W (ret);
-
- vec_free (args.name);
-
- return ret;
-}
-
-/* mrvl_pp2 create reply handler */
-static void
-vl_api_mrvl_pp2_create_reply_t_handler (vl_api_mrvl_pp2_create_reply_t * mp)
-{
- vat_main_t *vam = pp2_test_main.vat_main;
- i32 retval = ntohl (mp->retval);
-
- if (retval == 0)
- {
- fformat (vam->ofp, "created mrvl_pp2 with sw_if_index %d\n",
- ntohl (mp->sw_if_index));
- }
-
- vam->retval = retval;
- vam->result_ready = 1;
- vam->regenerate_interface_table = 1;
-}
-
-
-/* mrvl_pp2 delete API */
-static int
-api_mrvl_pp2_delete (vat_main_t * vam)
-{
- unformat_input_t *i = vam->input;
- //vnet_main_t *vnm = vnet_get_main ();
- vl_api_mrvl_pp2_delete_t *mp;
- u32 sw_if_index = 0;
- int ret;
-
- while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (i, "sw_if_index %d", &sw_if_index))
- ;
- else
- {
- clib_warning ("unknown input '%U'", format_unformat_error, i);
- return -99;
- }
- }
-
- M (MRVL_PP2_DELETE, mp);
-
- mp->sw_if_index = clib_host_to_net_u32 (sw_if_index);
-
- S (mp);
- W (ret);
-
- return ret;
-}
-
-#include <marvell/pp2/pp2.api_test.c>
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c
index 01b333a5234..3b981d69986 100644
--- a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c
+++ b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c
@@ -859,7 +859,7 @@ nat44_ei_icmp_in2out (vlib_buffer_t *b0, ip4_header_t *ip0,
nat44_ei_main_t *nm = &nat44_ei_main;
vlib_main_t *vm = vlib_get_main ();
ip4_address_t addr;
- u16 port;
+ u16 port = 0;
u32 fib_index;
nat_protocol_t proto;
icmp_echo_header_t *echo0, *inner_echo0 = 0;
diff --git a/src/plugins/netmap/CMakeLists.txt b/src/plugins/netmap/CMakeLists.txt
new file mode 100644
index 00000000000..d53a9e0911a
--- /dev/null
+++ b/src/plugins/netmap/CMakeLists.txt
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2024 Tom Jones <thj@freebsd.org>
+#
+# This software was developed by Tom Jones <thj@freebsd.org> under sponsorship
+# from the FreeBSD Foundation.
+#
+
+if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
+ message(WARNING "Netmap is only currently support on FreeBSD - netmap plugin disabled")
+ return()
+endif()
+
+add_vpp_plugin(netmap
+ SOURCES
+ plugin.c
+ netmap.c
+ node.c
+ device.c
+ cli.c
+ netmap_api.c
+
+ MULTIARCH_SOURCES
+ node.c
+ device.c
+
+ INSTALL_HEADERS
+ netmap.h
+ net_netmap.h
+
+ API_FILES
+ netmap.api
+)
diff --git a/extras/deprecated/netmap/FEATURE.yaml b/src/plugins/netmap/FEATURE.yaml
index e23e5c243e7..a9dfb2163e4 100644
--- a/extras/deprecated/netmap/FEATURE.yaml
+++ b/src/plugins/netmap/FEATURE.yaml
@@ -1,11 +1,11 @@
---
name: Netmap Device
-maintainer: Damjan Marion <damarion@cisco.com>
+maintainer: Tom Jones <thj@freebsd.org>
features:
- L4 checksum offload
description: "Create a netmap interface, which is a high speed user-space
- interface that allows VPP to patch into a linux namespace,
- a linux container, or a physical NIC without the use of DPDK."
+ interface that allows VPP to patch to a physical or virtual NIC
+ without the use of DPDK"
missing:
- API dump
state: production
diff --git a/extras/deprecated/netmap/cli.c b/src/plugins/netmap/cli.c
index 713632947a1..b54d397ecbe 100644
--- a/extras/deprecated/netmap/cli.c
+++ b/src/plugins/netmap/cli.c
@@ -22,8 +22,8 @@
#include <vlib/unix/unix.h>
#include <vnet/ethernet/ethernet.h>
-#include <vnet/devices/netmap/net_netmap.h>
-#include <vnet/devices/netmap/netmap.h>
+#include <netmap/net_netmap.h>
+#include <netmap/netmap.h>
static clib_error_t *
netmap_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
diff --git a/extras/deprecated/netmap/device.c b/src/plugins/netmap/device.c
index 47407aaaa26..505deb988c4 100644
--- a/extras/deprecated/netmap/device.c
+++ b/src/plugins/netmap/device.c
@@ -23,8 +23,8 @@
#include <vlib/unix/unix.h>
#include <vnet/ethernet/ethernet.h>
-#include <vnet/devices/netmap/net_netmap.h>
-#include <vnet/devices/netmap/netmap.h>
+#include <netmap/net_netmap.h>
+#include <netmap/netmap.h>
#define foreach_netmap_tx_func_error \
_(NO_FREE_SLOTS, "no free tx slots") \
diff --git a/extras/deprecated/netmap/net_netmap.h b/src/plugins/netmap/net_netmap.h
index fd4253b7c0c..ecccedd4484 100644
--- a/extras/deprecated/netmap/net_netmap.h
+++ b/src/plugins/netmap/net_netmap.h
@@ -39,10 +39,10 @@
#ifndef _NET_NETMAP_H_
#define _NET_NETMAP_H_
-#define NETMAP_API 11 /* current API version */
+#define NETMAP_API 14 /* current API version */
-#define NETMAP_MIN_API 11 /* min and max versions accepted */
-#define NETMAP_MAX_API 15
+#define NETMAP_MIN_API 14 /* min and max versions accepted */
+#define NETMAP_MAX_API 15
/*
* Some fields should be cache-aligned to reduce contention.
* The alignment is architecture and OS dependent, but rather than
diff --git a/extras/deprecated/netmap/netmap.api b/src/plugins/netmap/netmap.api
index a14753cad9c..a14753cad9c 100644
--- a/extras/deprecated/netmap/netmap.api
+++ b/src/plugins/netmap/netmap.api
diff --git a/extras/deprecated/netmap/netmap.c b/src/plugins/netmap/netmap.c
index 7cab6bb0289..ebef215eb3b 100644
--- a/extras/deprecated/netmap/netmap.c
+++ b/src/plugins/netmap/netmap.c
@@ -20,22 +20,17 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
-#include <vnet/devices/netmap/net_netmap.h>
#include <vlib/vlib.h>
#include <vlib/unix/unix.h>
#include <vnet/ethernet/ethernet.h>
-#include <vnet/devices/netmap/netmap.h>
-netmap_main_t netmap_main;
+#include <netmap/net_netmap.h>
+#include <netmap/netmap.h>
+#include <netmap/netmap.api_enum.h>
+#include <netmap/netmap.api_types.h>
-static u32
-netmap_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi,
- u32 flags)
-{
- /* nothing for now */
- return 0;
-}
+netmap_main_t netmap_main;
static clib_error_t *
netmap_fd_read_ready (clib_file_t * uf)
@@ -88,12 +83,11 @@ int
netmap_worker_thread_enable ()
{
/* if worker threads are enabled, switch to polling mode */
- foreach_vlib_main ((
- {
- vlib_node_set_state (this_vlib_main,
- netmap_input_node.index,
- VLIB_NODE_STATE_POLLING);
- }));
+ foreach_vlib_main ()
+ {
+ vlib_node_set_state (this_vlib_main, netmap_input_node.index,
+ VLIB_NODE_STATE_POLLING);
+ }
return 0;
}
@@ -101,12 +95,11 @@ netmap_worker_thread_enable ()
int
netmap_worker_thread_disable ()
{
- foreach_vlib_main ((
- {
- vlib_node_set_state (this_vlib_main,
- netmap_input_node.index,
- VLIB_NODE_STATE_INTERRUPT);
- }));
+ foreach_vlib_main ()
+ {
+ vlib_node_set_state (this_vlib_main, netmap_input_node.index,
+ VLIB_NODE_STATE_INTERRUPT);
+ }
return 0;
}
@@ -117,9 +110,9 @@ netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set,
{
netmap_main_t *nm = &netmap_main;
int ret = 0;
+ uint32_t nr_reg;
netmap_if_t *nif = 0;
u8 hw_addr[6];
- clib_error_t *error = 0;
vnet_sw_interface_t *sw;
vnet_main_t *vnm = vnet_get_main ();
uword *p;
@@ -179,10 +172,39 @@ netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set,
reg->refcnt++;
nif->nifp = NETMAP_IF (reg->mem, req->nr_offset);
- nif->first_rx_ring = 0;
- nif->last_rx_ring = 0;
- nif->first_tx_ring = 0;
- nif->last_tx_ring = 0;
+ nr_reg = nif->req->nr_flags & NR_REG_MASK;
+
+ if (nr_reg == NR_REG_SW)
+ { /* host stack */
+ nif->first_tx_ring = nif->last_tx_ring = nif->req->nr_tx_rings;
+ nif->first_rx_ring = nif->last_rx_ring = nif->req->nr_rx_rings;
+ }
+ else if (nr_reg == NR_REG_ALL_NIC)
+ { /* only nic */
+ nif->first_tx_ring = 0;
+ nif->first_rx_ring = 0;
+ nif->last_tx_ring = nif->req->nr_tx_rings - 1;
+ nif->last_rx_ring = nif->req->nr_rx_rings - 1;
+ }
+ else if (nr_reg == NR_REG_NIC_SW)
+ {
+ nif->first_tx_ring = 0;
+ nif->first_rx_ring = 0;
+ nif->last_tx_ring = nif->req->nr_tx_rings;
+ nif->last_rx_ring = nif->req->nr_rx_rings;
+ }
+ else if (nr_reg == NR_REG_ONE_NIC)
+ {
+ /* XXX check validity */
+ nif->first_tx_ring = nif->last_tx_ring = nif->first_rx_ring =
+ nif->last_rx_ring = nif->req->nr_ringid & NETMAP_RING_MASK;
+ }
+ else
+ { /* pipes */
+ nif->first_tx_ring = nif->last_tx_ring = 0;
+ nif->first_rx_ring = nif->last_rx_ring = 0;
+ }
+
nif->host_if_name = if_name;
nif->per_interface_next_index = ~0;
@@ -213,17 +235,14 @@ netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set,
hw_addr[1] = 0xfe;
}
- error = ethernet_register_interface (vnm, netmap_device_class.index,
- nif->if_index, hw_addr,
- &nif->hw_if_index,
- netmap_eth_flag_change);
+ vnet_eth_interface_registration_t eir = {};
- if (error)
- {
- clib_error_report (error);
- ret = VNET_API_ERROR_SYSCALL_ERROR_1;
- goto error;
- }
+ eir.dev_class_index = netmap_device_class.index;
+ eir.dev_instance = nif->if_index;
+ eir.address = hw_addr;
+ eir.cb.set_max_frame_size = NULL;
+
+ nif->hw_if_index = vnet_eth_register_interface (vnm, &eir);
sw = vnet_get_hw_sw_interface (vnm, nif->hw_if_index);
nif->sw_if_index = sw->sw_if_index;
diff --git a/extras/deprecated/netmap/netmap.h b/src/plugins/netmap/netmap.h
index 29f855fda8e..29f855fda8e 100644
--- a/extras/deprecated/netmap/netmap.h
+++ b/src/plugins/netmap/netmap.h
diff --git a/extras/deprecated/netmap/netmap_api.c b/src/plugins/netmap/netmap_api.c
index ee05ec22d25..51f572a23e6 100644
--- a/extras/deprecated/netmap/netmap_api.c
+++ b/src/plugins/netmap/netmap_api.c
@@ -22,23 +22,11 @@
#include <vnet/interface.h>
#include <vnet/api_errno.h>
-#include <vnet/devices/netmap/netmap.h>
+#include <netmap/netmap.h>
-#include <vnet/vnet_msg_enum.h>
-
-#define vl_typedefs /* define message structures */
-#include <vnet/vnet_all_api_h.h>
-#undef vl_typedefs
-
-#define vl_endianfun /* define message structures */
-#include <vnet/vnet_all_api_h.h>
-#undef vl_endianfun
-
-/* instantiate all the print functions we know about */
-#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
-#define vl_printfun
-#include <vnet/vnet_all_api_h.h>
-#undef vl_printfun
+#include <vnet/format_fns.h>
+#include <netmap/netmap.api_enum.h>
+#include <netmap/netmap.api_types.h>
#include <vlibapi/api_helper_macros.h>
@@ -84,44 +72,14 @@ vl_api_netmap_delete_t_handler (vl_api_netmap_delete_t * mp)
REPLY_MACRO (VL_API_NETMAP_DELETE_REPLY);
}
-/*
- * netmap_api_hookup
- * Add vpe's API message handlers to the table.
- * vlib has already mapped shared memory and
- * added the client registration handlers.
- * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process()
- */
-#define vl_msg_name_crc_list
-#include <vnet/vnet_all_api_h.h>
-#undef vl_msg_name_crc_list
-
-static void
-setup_message_id_table (api_main_t * am)
-{
-#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
- foreach_vl_msg_name_crc_netmap;
-#undef _
-}
-
+#include <netmap/netmap.api.c>
static clib_error_t *
netmap_api_hookup (vlib_main_t * vm)
{
- api_main_t *am = vlibapi_get_main ();
-
-#define _(N,n) \
- vl_msg_api_set_handlers(VL_API_##N, #n, \
- vl_api_##n##_t_handler, \
- vl_noop_handler, \
- vl_api_##n##_t_endian, \
- vl_api_##n##_t_print, \
- sizeof(vl_api_##n##_t), 1);
- foreach_vpe_api_msg;
-#undef _
-
/*
* Set up the (msg_name, crc, message-id) table
*/
- setup_message_id_table (am);
+ setup_message_id_table ();
return 0;
}
diff --git a/extras/deprecated/netmap/node.c b/src/plugins/netmap/node.c
index b0a68824b9c..6169847fa79 100644
--- a/extras/deprecated/netmap/node.c
+++ b/src/plugins/netmap/node.c
@@ -25,8 +25,8 @@
#include <vnet/devices/devices.h>
#include <vnet/feature/feature.h>
-#include <vnet/devices/netmap/net_netmap.h>
-#include <vnet/devices/netmap/netmap.h>
+#include <netmap/net_netmap.h>
+#include <netmap/netmap.h>
#define foreach_netmap_input_error
@@ -112,7 +112,7 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
n_free_bufs +=
vlib_buffer_alloc (vm, &nm->rx_buffers[thread_index][n_free_bufs],
VLIB_FRAME_SIZE);
- _vec_len (nm->rx_buffers[thread_index]) = n_free_bufs;
+ vec_set_len (nm->rx_buffers[thread_index], n_free_bufs);
}
cur_ring = nif->first_rx_ring;
@@ -166,7 +166,8 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
prev_bi0 = bi0;
bi0 = nm->rx_buffers[thread_index][last_empty_buffer];
b0 = vlib_get_buffer (vm, bi0);
- _vec_len (nm->rx_buffers[thread_index]) = last_empty_buffer;
+ vec_set_len (nm->rx_buffers[thread_index],
+ last_empty_buffer);
n_free_bufs--;
/* copy data */
@@ -200,11 +201,12 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
/* trace */
if (PREDICT_FALSE (n_trace > 0))
{
- if (PREDICT_TRUE (first_b0 != 0))
+ if (PREDICT_TRUE (first_b0 != 0) &&
+ vlib_trace_buffer (vm, node, next0, first_b0,
+ /* follow_chain */ 0))
{
netmap_input_trace_t *tr;
- vlib_trace_buffer (vm, node, next0, first_b0,
- /* follow_chain */ 0);
+
vlib_set_trace_count (vm, node, --n_trace);
tr = vlib_add_trace (vm, node, first_b0, sizeof (*tr));
tr->next_index = next0;
@@ -213,10 +215,6 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
}
}
- /* redirect if feature path enabled */
- vnet_feature_start_device_input_x1 (nif->sw_if_index, &next0,
- first_b0);
-
/* enque and take next packet */
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
n_left_to_next, first_bi0,
diff --git a/src/plugins/netmap/plugin.c b/src/plugins/netmap/plugin.c
new file mode 100644
index 00000000000..1673225b683
--- /dev/null
+++ b/src/plugins/netmap/plugin.c
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Tom Jones <thj@freebsd.org>
+ *
+ * This software was developed by Tom Jones <thj@freebsd.org> under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "netmap",
+};
diff --git a/src/plugins/npt66/npt66.api b/src/plugins/npt66/npt66.api
index 63640ac2097..dab09cda31f 100644
--- a/src/plugins/npt66/npt66.api
+++ b/src/plugins/npt66/npt66.api
@@ -36,5 +36,16 @@ counters npt66 {
units "packets";
description "packet translation failed";
};
-
+ icmp6_checksum {
+ severity error;
+ type counter64;
+ units "packets";
+ description "ICMP6 checksum validation failed";
+ };
+ icmp6_truncated {
+ severity error;
+ type counter64;
+ units "packets";
+ description "ICMP6 packet truncated";
+ };
}; \ No newline at end of file
diff --git a/src/plugins/npt66/npt66_node.c b/src/plugins/npt66/npt66_node.c
index f74f9143998..0d0c475f2c3 100644
--- a/src/plugins/npt66/npt66_node.c
+++ b/src/plugins/npt66/npt66_node.c
@@ -127,10 +127,7 @@ npt66_translate (ip6_header_t *ip, npt66_binding_t *binding, int dir)
if (!ip6_prefix_cmp (ip->src_address, binding->internal,
binding->internal_plen))
{
- clib_warning (
- "npt66_translate: src address is not internal (%U -> %U)",
- format_ip6_address, &ip->src_address, format_ip6_address,
- &ip->dst_address);
+ /* Packet is not for us */
goto done;
}
ip->src_address = ip6_prefix_copy (ip->src_address, binding->external,
@@ -144,10 +141,7 @@ npt66_translate (ip6_header_t *ip, npt66_binding_t *binding, int dir)
if (!ip6_prefix_cmp (ip->dst_address, binding->external,
binding->external_plen))
{
- clib_warning (
- "npt66_translate: dst address is not external (%U -> %U)",
- format_ip6_address, &ip->src_address, format_ip6_address,
- &ip->dst_address);
+ /* Packet is not for us */
goto done;
}
ip->dst_address = ip6_prefix_copy (ip->dst_address, binding->internal,
@@ -162,7 +156,7 @@ done:
static int
npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip,
icmp46_header_t *icmp, npt66_binding_t *binding,
- int dir)
+ int dir, u32 *error)
{
ip6_header_t *ip = (ip6_header_t *) (icmp + 2);
int rv = 0;
@@ -171,7 +165,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip,
if (clib_net_to_host_u16 (outer_ip->payload_length) <
sizeof (icmp46_header_t) + 4 + sizeof (ip6_header_t))
{
- clib_warning ("ICMP6 payload too short");
+ *error = NPT66_ERROR_ICMP6_TRUNCATED;
return -1;
}
@@ -181,7 +175,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip,
sum16 = ip6_tcp_udp_icmp_compute_checksum (vm, b, outer_ip, &bogus_length);
if (sum16 != 0 && sum16 != 0xffff)
{
- clib_warning ("ICMP6 checksum failed");
+ *error = NPT66_ERROR_ICMP6_CHECKSUM;
return -1;
}
if (dir == VLIB_RX)
@@ -189,10 +183,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip,
if (!ip6_prefix_cmp (ip->src_address, binding->external,
binding->external_plen))
{
- clib_warning (
- "npt66_icmp6_translate: src address is not internal (%U -> %U)",
- format_ip6_address, &ip->src_address, format_ip6_address,
- &ip->dst_address);
+ /* Not for us */
goto done;
}
ip->src_address = ip6_prefix_copy (ip->src_address, binding->internal,
@@ -206,10 +197,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip,
if (!ip6_prefix_cmp (ip->dst_address, binding->external,
binding->external_plen))
{
- clib_warning (
- "npt66_icmp6_translate: dst address is not external (%U -> %U)",
- format_ip6_address, &ip->src_address, format_ip6_address,
- &ip->dst_address);
+ /* Not for us */
goto done;
}
ip->dst_address = ip6_prefix_copy (ip->dst_address, binding->internal,
@@ -217,8 +205,8 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip,
rv = npt66_adjust_checksum (binding->internal_plen, false,
binding->delta, &ip->dst_address);
}
-done:
+done:
return rv;
}
@@ -243,10 +231,12 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
n_left_from = frame->n_vectors;
vlib_get_buffers (vm, from, b, n_left_from);
npt66_binding_t *binding;
+ u32 translated = 0;
/* Stage 1: build vector of flow hash (based on lookup mask) */
while (n_left_from > 0)
{
+ u32 error = NPT66_ERROR_TRANSLATION;
u32 sw_if_index = vnet_buffer (b[0])->sw_if_index[dir];
u32 iph_offset =
dir == VLIB_TX ? vnet_buffer (b[0])->ip.save_rewrite_length : 0;
@@ -261,28 +251,26 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
icmp46_header_t *icmp = (icmp46_header_t *) (ip + 1);
if (ip->protocol == IP_PROTOCOL_ICMP6 && icmp->type < 128)
{
- rv = npt66_icmp6_translate (b[0], ip, icmp, binding, dir);
+ rv = npt66_icmp6_translate (b[0], ip, icmp, binding, dir, &error);
if (rv < 0)
{
- clib_warning ("ICMP6 npt66_translate failed");
*next = NPT66_NEXT_DROP;
+ b[0]->error = node->errors[error];
goto next;
}
}
- rv = npt66_translate (ip, binding, dir);
+ rv = npt66_translate (ip, binding, dir);
if (rv < 0)
{
- vlib_node_increment_counter (vm, node->node_index,
- NPT66_ERROR_TRANSLATION, 1);
+ b[0]->error = node->errors[error];
*next = NPT66_NEXT_DROP;
goto next;
}
- else if (dir == VLIB_TX)
- vlib_node_increment_counter (vm, node->node_index, NPT66_ERROR_TX, 1);
else
- vlib_node_increment_counter (vm, node->node_index, NPT66_ERROR_RX, 1);
-
+ {
+ translated++;
+ }
next:
next += 1;
n_left_from -= 1;
@@ -321,6 +309,9 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
break;
}
}
+ vlib_node_increment_counter (
+ vm, node->node_index, dir == VLIB_TX ? NPT66_ERROR_TX : NPT66_ERROR_RX,
+ translated);
vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
return frame->n_vectors;
@@ -338,17 +329,17 @@ VLIB_NODE_FN (npt66_output_node)
}
VLIB_REGISTER_NODE(npt66_input_node) = {
- .name = "npt66-input",
- .vector_size = sizeof(u32),
- .format_trace = format_npt66_trace,
- .type = VLIB_NODE_TYPE_INTERNAL,
- .n_errors = NPT66_N_ERROR,
- .error_counters = npt66_error_counters,
- .n_next_nodes = NPT66_N_NEXT,
- .next_nodes =
- {
- [NPT66_NEXT_DROP] = "error-drop",
- },
+ .name = "npt66-input",
+ .vector_size = sizeof(u32),
+ .format_trace = format_npt66_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = NPT66_N_ERROR,
+ .error_counters = npt66_error_counters,
+ .n_next_nodes = NPT66_N_NEXT,
+ .next_nodes =
+ {
+ [NPT66_NEXT_DROP] = "error-drop",
+ },
};
VLIB_REGISTER_NODE (npt66_output_node) = {
diff --git a/src/plugins/builtinurl/CMakeLists.txt b/src/plugins/osi/CMakeLists.txt
index ddbca5e50f1..8ab014770ea 100644
--- a/src/plugins/builtinurl/CMakeLists.txt
+++ b/src/plugins/osi/CMakeLists.txt
@@ -1,5 +1,4 @@
-
-# Copyright (c) <current-year> <your-organization>
+# Copyright (c) 2023 Cisco and/or its affiliates
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -12,15 +11,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-add_vpp_plugin(builtinurl
- SOURCES
- builtins.c
- builtinurl.c
- builtinurl.h
+add_vpp_plugin(osi
- API_FILES
- builtinurl.api
+ SOURCES
+ osi.c
+ node.c
+ pg.c
+ plugin.c
- API_TEST_SOURCES
- builtinurl_test.c
+ INSTALL_HEADERS
+ osi.h
)
diff --git a/src/plugins/osi/FEATURE.yaml b/src/plugins/osi/FEATURE.yaml
new file mode 100644
index 00000000000..337be1c7146
--- /dev/null
+++ b/src/plugins/osi/FEATURE.yaml
@@ -0,0 +1,11 @@
+---
+name: OSI plugin
+maintainer:
+ - community <vpp-dev@lists.fd.io>
+features:
+ - Adds support for OSI protocols (SAP types)
+ - Registered as input protocol for PPP, HDLC, and LLC
+missing:
+ - No tests for this feature currently exist
+description: ""
+state: experimental
diff --git a/src/vnet/osi/node.c b/src/plugins/osi/node.c
index 9edc354cda7..a36b1525e0e 100644
--- a/src/vnet/osi/node.c
+++ b/src/plugins/osi/node.c
@@ -39,7 +39,7 @@
#include <vlib/vlib.h>
#include <vnet/pg/pg.h>
-#include <vnet/osi/osi.h>
+#include <osi/osi.h>
#include <vnet/ppp/ppp.h>
#include <vnet/hdlc/hdlc.h>
#include <vnet/llc/llc.h>
diff --git a/src/vnet/osi/osi.c b/src/plugins/osi/osi.c
index 9556481448a..67c7053f388 100644
--- a/src/vnet/osi/osi.c
+++ b/src/plugins/osi/osi.c
@@ -38,7 +38,7 @@
*/
#include <vnet/vnet.h>
-#include <vnet/osi/osi.h>
+#include <osi/osi.h>
/* Global main structure. */
osi_main_t osi_main;
@@ -169,13 +169,8 @@ add_protocol (osi_main_t * pm, osi_protocol_t protocol, char *protocol_name)
static clib_error_t *
osi_init (vlib_main_t * vm)
{
- clib_error_t *error = 0;
osi_main_t *pm = &osi_main;
- /* init order dependency: llc_init -> osi_init */
- if ((error = vlib_call_init_function (vm, llc_init)))
- return error;
-
clib_memset (pm, 0, sizeof (pm[0]));
pm->vlib_main = vm;
@@ -189,8 +184,11 @@ osi_init (vlib_main_t * vm)
return vlib_call_init_function (vm, osi_input_init);
}
-VLIB_INIT_FUNCTION (osi_init);
-
+/* init order dependency: llc_init -> osi_init -> snap_init*/
+/* Otherwise, osi_input_init will wipe out e.g. the snap init */
+VLIB_INIT_FUNCTION (osi_init) = {
+ .init_order = VLIB_INITS ("llc_init", "osi_init", "snap_init"),
+};
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/vnet/osi/osi.h b/src/plugins/osi/osi.h
index fb248ed9cc5..fb248ed9cc5 100644
--- a/src/vnet/osi/osi.h
+++ b/src/plugins/osi/osi.h
diff --git a/src/vnet/osi/pg.c b/src/plugins/osi/pg.c
index c87a869b28d..3bac693c127 100644
--- a/src/vnet/osi/pg.c
+++ b/src/plugins/osi/pg.c
@@ -39,7 +39,7 @@
#include <vlib/vlib.h>
#include <vnet/pg/pg.h>
-#include <vnet/osi/osi.h>
+#include <osi/osi.h>
typedef struct
{
diff --git a/extras/deprecated/netmap/dir.dox b/src/plugins/osi/plugin.c
index 7ddbf947c29..5fc412e093e 100644
--- a/extras/deprecated/netmap/dir.dox
+++ b/src/plugins/osi/plugin.c
@@ -1,6 +1,7 @@
/*
- * Copyright (c) 2017 Cisco and/or its affiliates.
+ * plugin.c: osi
*
+ * Copyright (c) 2023 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -13,15 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-/* Doxygen directory documentation */
-
-/**
-@dir
-@brief netmap Interface Implementation.
-
-This directory contains the source code for the netmap driver.
-
-*/
-/*? %%clicmd:group_label netmap %% ?*/
-/*? %%syscfg:group_label netmap %% ?*/
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "OSI plugin",
+}; \ No newline at end of file
diff --git a/src/plugins/prom/prom.c b/src/plugins/prom/prom.c
index 934e8480d3c..475e98b1038 100644
--- a/src/plugins/prom/prom.c
+++ b/src/plugins/prom/prom.c
@@ -191,6 +191,7 @@ send_data_to_hss (hss_session_handle_t sh)
args.sh = sh;
args.data = vec_dup (pm->stats);
args.data_len = vec_len (pm->stats);
+ args.ct = HTTP_CONTENT_TEXT_PLAIN;
args.sc = HTTP_STATUS_OK;
args.free_vec_data = 1;
@@ -207,7 +208,7 @@ static uword
prom_scraper_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
vlib_frame_t *f)
{
- uword *event_data = 0, event_type;
+ uword *event_data = 0, event_type, *sh_as_uword;
prom_main_t *pm = &prom_main;
hss_session_handle_t sh;
f64 timeout = 10000.0;
@@ -222,12 +223,15 @@ prom_scraper_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
/* timeout, do nothing */
break;
case PROM_SCRAPER_EVT_RUN:
- sh.as_u64 = event_data[0];
vec_reset_length (pm->stats);
pm->stats = scrape_stats_segment (pm->stats, pm->stats_patterns,
pm->used_only);
- session_send_rpc_evt_to_thread_force (sh.thread_index,
- send_data_to_hss_rpc, &sh);
+ vec_foreach (sh_as_uword, event_data)
+ {
+ sh.as_u64 = (u64) *sh_as_uword;
+ session_send_rpc_evt_to_thread_force (
+ sh.thread_index, send_data_to_hss_rpc, sh_as_uword);
+ }
pm->last_scrape = vlib_time_now (vm);
break;
default:
diff --git a/src/plugins/pvti/CMakeLists.txt b/src/plugins/pvti/CMakeLists.txt
new file mode 100644
index 00000000000..900b662d54a
--- /dev/null
+++ b/src/plugins/pvti/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Copyright (c) 2024 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_vpp_plugin(pvti
+ SOURCES
+ pvti_if.c
+ pvti.c
+ input.h
+ input.c
+ input-main.c
+ output.h
+ output.c
+ output-main.c
+ bypass.h
+ bypass.c
+ bypass-main.c
+ api.c
+ pvti.h
+
+ MULTIARCH_SOURCES
+ input.c
+ output.c
+ bypass.c
+
+ API_FILES
+ pvti.api
+
+ # API_TEST_SOURCES
+ # pvti_test.c
+)
diff --git a/src/plugins/pvti/FEATURE.yaml b/src/plugins/pvti/FEATURE.yaml
new file mode 100644
index 00000000000..52dbe5b7c1b
--- /dev/null
+++ b/src/plugins/pvti/FEATURE.yaml
@@ -0,0 +1,8 @@
+---
+name: Packet Vector Tunnel
+maintainer: Andrew Yourtchenko <ayourtch@gmail.com>
+features:
+ - support inner MTU up to ~8K over standard 1280..1500 MTU substrate
+description: "Large MTU Tunnels"
+state: development
+properties: [API, CLI]
diff --git a/src/plugins/pvti/api.c b/src/plugins/pvti/api.c
new file mode 100644
index 00000000000..cda39ad44e8
--- /dev/null
+++ b/src/plugins/pvti/api.c
@@ -0,0 +1,137 @@
+
+#include <vnet/vnet.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/format_fns.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vlibapi/api.h>
+
+#include <pvti/pvti.api_enum.h>
+#include <pvti/pvti.api_types.h>
+
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+
+#define REPLY_MSG_ID_BASE pvm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+typedef struct
+{
+ vl_api_registration_t *reg;
+ u32 context;
+} pvti_if_details_ctx_t;
+
+typedef struct
+{
+
+} pvti_interface_dump_ctx_t;
+
+static walk_rc_t
+pvti_if_send_details (index_t pvtii, void *data)
+{
+ vl_api_pvti_interface_details_t *rmp;
+ pvti_if_details_ctx_t *ctx = data;
+ const pvti_if_t *pvi;
+
+ pvi = pvti_if_get (pvtii);
+
+ rmp = vl_msg_api_alloc_zero (sizeof (*rmp));
+ rmp->_vl_msg_id =
+ htons (VL_API_PVTI_INTERFACE_DETAILS + pvti_main.msg_id_base);
+
+ rmp->interface.sw_if_index = htonl (pvi->sw_if_index);
+ rmp->interface.local_port = htons (pvi->local_port);
+ rmp->interface.remote_port = htons (pvi->remote_port);
+ rmp->interface.underlay_mtu = htons (pvi->underlay_mtu);
+
+ ip_address_encode2 (&pvi->local_ip, &rmp->interface.local_ip);
+ ip_address_encode2 (&pvi->remote_ip, &rmp->interface.remote_ip);
+
+ rmp->context = ctx->context;
+
+ vl_api_send_msg (ctx->reg, (u8 *) rmp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_pvti_interface_dump_t_handler (vl_api_pvti_interface_dump_t *mp)
+{
+ vl_api_registration_t *reg;
+ // pvti_main_t *pvm = &pvti_main;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (reg == 0)
+ return;
+
+ pvti_if_details_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+ if (sw_if_index == ~0)
+ pvti_if_walk (pvti_if_send_details, &ctx);
+ else
+ {
+ index_t pvtii = pvti_if_find_by_sw_if_index (sw_if_index);
+ if (pvtii != INDEX_INVALID)
+ pvti_if_send_details (pvtii, &ctx);
+ }
+}
+
+static void
+vl_api_pvti_interface_create_t_handler (vl_api_pvti_interface_create_t *mp)
+{
+ vl_api_pvti_interface_create_reply_t *rmp;
+ pvti_main_t *pvm = &pvti_main;
+ int rv = ~0;
+ u32 sw_if_index = ~0;
+ ip_address_t local_ip;
+ ip_address_t remote_ip;
+
+ ip_address_decode2 (&mp->interface.local_ip, &local_ip);
+ ip_address_decode2 (&mp->interface.remote_ip, &remote_ip);
+ u16 lport = clib_host_to_net_u16 (mp->interface.local_port);
+ u16 rport = clib_host_to_net_u16 (mp->interface.remote_port);
+ u16 underlay_mtu = clib_host_to_net_u16 (mp->interface.underlay_mtu);
+ u32 underlay_fib_index =
+ clib_host_to_net_u32 (mp->interface.underlay_fib_index);
+ pvti_peer_address_method_t peer_address_method =
+ mp->interface.peer_address_from_payload ? PVTI_PEER_ADDRESS_FROM_PAYLOAD :
+ PVTI_PEER_ADDRESS_FIXED;
+
+ if (underlay_mtu == 0)
+ {
+ underlay_mtu = 1500;
+ }
+
+ rv =
+ pvti_if_create (&local_ip, lport, &remote_ip, rport, peer_address_method,
+ underlay_mtu, underlay_fib_index, &sw_if_index);
+
+ REPLY_MACRO2 (VL_API_PVTI_INTERFACE_CREATE_REPLY,
+ { rmp->sw_if_index = htonl (sw_if_index); });
+}
+
+static void
+vl_api_pvti_interface_delete_t_handler (vl_api_pvti_interface_delete_t *mp)
+{
+ vl_api_pvti_interface_delete_reply_t *rmp;
+ pvti_main_t *pvm = &pvti_main;
+ int rv = 0;
+
+ rv = pvti_if_delete (ntohl (mp->sw_if_index));
+ REPLY_MACRO (VL_API_PVTI_INTERFACE_DELETE_REPLY);
+}
+
+/* API definitions */
+#include <pvti/pvti.api.c>
+
+void
+pvti_api_init ()
+{
+ pvti_main_t *pvm = &pvti_main;
+ /* Add our API messages to the global name_crc hash table */
+ pvm->msg_id_base = setup_message_id_table ();
+}
diff --git a/src/plugins/pvti/bypass-main.c b/src/plugins/pvti/bypass-main.c
new file mode 100644
index 00000000000..db79ccd2113
--- /dev/null
+++ b/src/plugins/pvti/bypass-main.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pvti/bypass.h>
+
+/* packet trace format function */
+static u8 *
+format_pvti_bypass_trace (u8 *s, va_list *args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ pvti_bypass_trace_t *t = va_arg (*args, pvti_bypass_trace_t *);
+
+ s = format (s, "PVTI-BYPASS: sw_if_index %d, next index %d\n",
+ t->sw_if_index, t->next_index);
+ s = format (s, " src %U sport %d dport %d\n", format_ip_address,
+ &t->remote_ip, t->remote_port, t->local_port);
+ s = format (s, " seq: %d", t->seq);
+ return s;
+}
+
+vlib_node_registration_t pvti4_bypass_node;
+vlib_node_registration_t pvti6_bypass_node;
+
+static char *pvti_bypass_error_strings[] = {
+#define _(sym, string) string,
+ foreach_pvti_bypass_error
+#undef _
+};
+
+VLIB_REGISTER_NODE (pvti4_bypass_node) =
+{
+ .name = "ip4-pvti-bypass",
+ .vector_size = sizeof (u32),
+ .format_trace = format_pvti_bypass_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(pvti_bypass_error_strings),
+ .error_strings = pvti_bypass_error_strings,
+
+ .n_next_nodes = PVTI_BYPASS_N_NEXT,
+
+ .next_nodes = {
+ [PVTI_BYPASS_NEXT_DROP] = "error-drop",
+ [PVTI_BYPASS_NEXT_PVTI_INPUT] = "pvti4-input",
+ },
+
+};
+
+VLIB_REGISTER_NODE (pvti6_bypass_node) =
+{
+ .name = "ip6-pvti-bypass",
+ .vector_size = sizeof (u32),
+ .format_trace = format_pvti_bypass_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(pvti_bypass_error_strings),
+ .error_strings = pvti_bypass_error_strings,
+
+ .n_next_nodes = PVTI_BYPASS_N_NEXT,
+
+ .next_nodes = {
+ [PVTI_BYPASS_NEXT_DROP] = "error-drop",
+ [PVTI_BYPASS_NEXT_PVTI_INPUT] = "pvti6-input",
+ },
+
+};
diff --git a/src/plugins/pvti/bypass.c b/src/plugins/pvti/bypass.c
new file mode 100644
index 00000000000..14c976439eb
--- /dev/null
+++ b/src/plugins/pvti/bypass.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+#include <pvti/bypass.h>
+
+always_inline u16
+pvti_bypass_node_common (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, bool is_ip6)
+{
+ u32 n_left_from, *from, *to_next;
+ pvti_bypass_next_t next_index;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip4_input_node.index);
+
+ u32 pkts_processed = 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t *b0;
+ u32 sw_if_index0 = 0;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ udp_header_t *udp0;
+ u32 bi0, ip_len0, udp_len0, flags0, next0;
+ u8 error0, good_udp0, proto0;
+ i32 len_diff0;
+
+ bi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next += 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ /* setup the packet for the next feature */
+ vnet_feature_next (&next0, b0);
+
+ if (is_ip6)
+ {
+ ip60 = vlib_buffer_get_current (b0);
+ }
+ else
+ {
+ ip40 = vlib_buffer_get_current (b0);
+ }
+
+ if (is_ip6)
+ {
+ proto0 = ip60->protocol;
+ }
+ else
+ {
+ /* Treat IP frag packets as "experimental" protocol for now */
+ proto0 = ip4_is_fragment (ip40) ? 0xfe : ip40->protocol;
+ }
+
+ /* Process packet 0 */
+ if (proto0 != IP_PROTOCOL_UDP)
+ goto exit; /* not UDP packet */
+
+ if (is_ip6)
+ udp0 = ip6_next_header (ip60);
+ else
+ udp0 = ip4_next_header (ip40);
+
+ /* look up the destination ip and port */
+ u32 pvti_index0 = INDEX_INVALID;
+ if (is_ip6)
+ {
+ pvti_index0 = pvti_if_find_by_remote_ip6_and_port (
+ &ip60->src_address, clib_net_to_host_u16 (udp0->src_port));
+ }
+ else
+ {
+ pvti_index0 = pvti_if_find_by_remote_ip4_and_port (
+ &ip40->src_address, clib_net_to_host_u16 (udp0->src_port));
+ }
+ if (pvti_index0 == INDEX_INVALID)
+ goto exit;
+
+ flags0 = b0->flags;
+ good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
+
+ /* Don't verify UDP checksum for packets with explicit zero checksum.
+ */
+ good_udp0 |= udp0->checksum == 0;
+
+ /* Verify UDP length */
+ if (is_ip6)
+ ip_len0 = clib_net_to_host_u16 (ip60->payload_length);
+ else
+ ip_len0 = clib_net_to_host_u16 (ip40->length);
+ udp_len0 = clib_net_to_host_u16 (udp0->length);
+ len_diff0 = ip_len0 - udp_len0;
+
+ /* Verify UDP checksum */
+ if (PREDICT_FALSE (!good_udp0))
+ {
+ if (is_ip6)
+ flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
+ else
+ flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
+ good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
+ }
+
+ if (is_ip6)
+ {
+ error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
+ error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
+ }
+ else
+ {
+ error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
+ error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
+ }
+
+ next0 = error0 ? PVTI_BYPASS_NEXT_DROP : PVTI_BYPASS_NEXT_PVTI_INPUT;
+ b0->error = error0 ? error_node->errors[error0] : 0;
+
+ /* pvtiX-input node expect current at PVTI header */
+ if (is_ip6)
+ vlib_buffer_advance (b0, sizeof (ip6_header_t) +
+ sizeof (udp_header_t));
+ else
+ vlib_buffer_advance (b0, sizeof (ip4_header_t) +
+ sizeof (udp_header_t));
+ exit:
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ pvti_bypass_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ t->seq = 0; // clib_net_to_host_u32 (pvti0->seq);
+ if (is_ip6)
+ {
+ }
+ else
+ {
+ t->remote_ip.ip.ip4 = ip40->src_address;
+ t->remote_ip.version = AF_IP4;
+ }
+ // t->local_port = h0->udp.dst_port;
+ // t->remote_port = h0->udp.src_port;
+ }
+
+ pkts_processed += 1;
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_node_increment_counter (vm, node->node_index,
+ PVTI_BYPASS_ERROR_PROCESSED, pkts_processed);
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (pvti4_bypass_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return pvti_bypass_node_common (vm, node, frame, 0);
+}
+
+VLIB_NODE_FN (pvti6_bypass_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return pvti_bypass_node_common (vm, node, frame, 1);
+}
diff --git a/src/plugins/pvti/bypass.h b/src/plugins/pvti/bypass.h
new file mode 100644
index 00000000000..611d5770ad3
--- /dev/null
+++ b/src/plugins/pvti/bypass.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_pvti_bypass_h__
+#define __included_pvti_bypass_h__
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+
+typedef struct
+{
+ u32 next_index;
+ u32 sw_if_index;
+ ip_address_t remote_ip;
+ u16 remote_port;
+ u16 local_port;
+ u32 seq;
+} pvti_bypass_trace_t;
+
+#define foreach_pvti_bypass_error \
+ _ (PROCESSED, "PVTI bypass tunnel packets processed")
+
+typedef enum
+{
+#define _(sym, str) PVTI_BYPASS_ERROR_##sym,
+ foreach_pvti_bypass_error
+#undef _
+ PVTI_BYPASS_N_ERROR,
+} pvti_bypass_error_t;
+
+typedef enum
+{
+ PVTI_BYPASS_NEXT_DROP,
+ PVTI_BYPASS_NEXT_PVTI_INPUT,
+ PVTI_BYPASS_N_NEXT,
+} pvti_bypass_next_t;
+
+#endif // pvti_bypass_h
diff --git a/src/plugins/pvti/input-main.c b/src/plugins/pvti/input-main.c
new file mode 100644
index 00000000000..8ab8b18dd7c
--- /dev/null
+++ b/src/plugins/pvti/input-main.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pvti/input.h>
+
+static char *pvti_input_error_strings[] = {
+#define _(sym, string) string,
+ foreach_pvti_input_error
+#undef _
+};
+
+#define _(f, s) s,
+static char *pvti_input_trace_type_names[] = { foreach_pvti_input_trace_type };
+#undef _
+
+static char *
+get_pvti_trace_type_name (u8 ptype)
+{
+ if (ptype < PVTI_INPUT_TRACE_N_TYPES)
+ {
+ return pvti_input_trace_type_names[ptype];
+ }
+ else
+ {
+ return "unknown";
+ }
+}
+
+/* packet trace format function */
+static u8 *
+format_pvti_input_trace (u8 *s, va_list *args)
+{
+ int i;
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ pvti_input_trace_t *t = va_arg (*args, pvti_input_trace_t *);
+
+ u32 indent = format_get_indent (s);
+
+ s = format (s,
+ "PVTI-IN: sw_if_index %d, next index %d, trace_type: %s(%d), "
+ "chunkcnt: %d\n",
+ t->sw_if_index, t->next_index,
+ get_pvti_trace_type_name (t->trace_type), t->trace_type,
+ t->chunk_count);
+ s = format (s, " src %U sport %d dport %d\n", format_ip_address,
+ &t->remote_ip, t->remote_port, t->local_port);
+ s = format (s, " seq: %d, chunk_count: %d\n", t->seq, t->chunk_count);
+ u16 max = t->chunk_count > MAX_CHUNKS ? MAX_CHUNKS : t->chunk_count;
+ for (i = 0; i < max; i++)
+ {
+ s = format (s, " %02d: sz %d\n", i, t->chunks[i].total_chunk_length);
+ }
+ s = format (s, "\n%U%U", format_white_space, indent,
+ format_ip_adjacency_packet_data, t->packet_data,
+ sizeof (t->packet_data));
+
+ return s;
+}
+
+vlib_node_registration_t pvti4_input_node;
+vlib_node_registration_t pvti6_input_node;
+
+VLIB_REGISTER_NODE (pvti4_input_node) =
+{
+ .name = "pvti4-input",
+ .vector_size = sizeof (u32),
+ .format_trace = format_pvti_input_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(pvti_input_error_strings),
+ .error_strings = pvti_input_error_strings,
+
+ .n_next_nodes = PVTI_INPUT_N_NEXT,
+
+ .next_nodes = {
+ [PVTI_INPUT_NEXT_DROP] = "error-drop",
+ [PVTI_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
+ [PVTI_INPUT_NEXT_IP6_INPUT] = "ip6-input",
+ [PVTI_INPUT_NEXT_PUNT] = "error-punt",
+ },
+
+};
+VLIB_REGISTER_NODE (pvti6_input_node) =
+{
+ .name = "pvti6-input",
+ .vector_size = sizeof (u32),
+ .format_trace = format_pvti_input_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(pvti_input_error_strings),
+ .error_strings = pvti_input_error_strings,
+
+ .n_next_nodes = PVTI_INPUT_N_NEXT,
+
+ .next_nodes = {
+ [PVTI_INPUT_NEXT_DROP] = "error-drop",
+ [PVTI_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
+ [PVTI_INPUT_NEXT_IP6_INPUT] = "ip6-input",
+ [PVTI_INPUT_NEXT_PUNT] = "error-punt",
+ },
+
+};
diff --git a/src/plugins/pvti/input.c b/src/plugins/pvti/input.c
new file mode 100644
index 00000000000..6a8806e2795
--- /dev/null
+++ b/src/plugins/pvti/input.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+#include <pvti/input.h>
+
+always_inline void
+pvti_enqueue_rx_bi_to_next_and_trace (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ pvti_per_thread_data_t *ptd, u32 bi0,
+ u16 next0)
+{
+ vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
+
+ if (PREDICT_TRUE (vlib_trace_buffer (vm, node, next0, b0,
+ /* follow_chain */ 0)))
+ {
+ pvti_input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ t->trace_type = PVTI_INPUT_TRACE_decap;
+ clib_memcpy (t->packet_data, vlib_buffer_get_current (b0),
+ sizeof (t->packet_data));
+ }
+ vec_add1 (ptd->pending_rx_buffers, bi0);
+ vec_add1 (ptd->pending_rx_nexts, next0);
+}
+
+always_inline pvti_rx_peer_t *
+pvti_try_find_or_create_rx_peer (pvti_per_thread_data_t *ptd,
+ vlib_buffer_t *b0, bool is_ip6)
+{
+ pvti_rx_peer_t *peer;
+
+ ip_address_t remote_ip = { 0 };
+ u16 remote_port;
+ if (is_ip6)
+ {
+ pvti_ip6_encap_header_t *h0 =
+ ((pvti_ip6_encap_header_t *) vlib_buffer_get_current (b0)) - 1;
+ ip_address_set (&remote_ip, &h0->ip6.src_address, AF_IP6);
+ remote_port = clib_net_to_host_u16 (h0->udp.src_port);
+ }
+ else
+ {
+ pvti_ip4_encap_header_t *h0 =
+ ((pvti_ip4_encap_header_t *) vlib_buffer_get_current (b0)) - 1;
+ ip_address_set (&remote_ip, &h0->ip4.src_address, AF_IP4);
+ remote_port = clib_net_to_host_u16 (h0->udp.src_port);
+ }
+
+ pool_foreach (peer, ptd->rx_peers)
+ {
+ if (peer->remote_port == remote_port &&
+ 0 == ip_address_cmp (&remote_ip, &peer->remote_ip))
+ {
+ if (peer->deleted)
+ {
+ // The peer has been marked as deleted - wipe it.
+ clib_memset (peer, 0xca, sizeof (*peer));
+ pool_put (ptd->rx_peers, peer);
+ continue;
+ }
+ return peer;
+ }
+ }
+
+ index_t pvti_if_index0 =
+ pvti_if_find_by_remote_ip_and_port (&remote_ip, remote_port);
+ if (INDEX_INVALID == pvti_if_index0)
+ {
+ // no suitable interface found, bail
+ return 0;
+ }
+ pvti_if_t *pvti_if0 = pvti_if_get (pvti_if_index0);
+
+ pvti_rx_peer_t new_peer = {
+ .local_ip = pvti_if0->local_ip,
+ .local_port = pvti_if0->local_port,
+ .remote_ip = remote_ip,
+ .remote_port = remote_port,
+ .pvti_if_index = pvti_if_index0,
+ .rx_streams = { { 0 } },
+ };
+ pvti_rx_peer_t *rx_new_peer;
+ pool_get (ptd->rx_peers, rx_new_peer);
+ *rx_new_peer = new_peer;
+
+ int i;
+ for (i = 0; i < MAX_RX_STREAMS; i++)
+ {
+ rx_new_peer->rx_streams[i].rx_bi0 = INDEX_INVALID;
+ rx_new_peer->rx_streams[i].rx_bi0_first = INDEX_INVALID;
+ rx_new_peer->rx_streams[i].rx_next0 = 0;
+ }
+
+ return rx_new_peer;
+}
+
+always_inline u16
+pvti_input_node_common (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, bool is_ip6)
+{
+ u32 n_left_from, *from;
+ pvti_chunk_header_t *chunks[MAX_CHUNKS];
+ u32 pkts_processed = 0;
+ u32 pkts_decapsulated = 0;
+ u32 decap_failed_no_buffers = 0;
+
+ pvti_main_t *pvm = &pvti_main;
+
+ u32 thread_index = vlib_get_thread_index ();
+ pvti_per_thread_data_t *ptd =
+ vec_elt_at_index (pvm->per_thread_data[is_ip6], thread_index);
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+
+ while (n_left_from > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u32 next0 = PVTI_INPUT_NEXT_DROP;
+ u32 sw_if_index0;
+ u8 true_chunk_count = 0;
+ u8 max_chunk_count;
+
+ bi0 = from[0];
+ from += 1;
+ n_left_from -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ pvti_ip4_encap_header_t *h0 =
+ ((pvti_ip4_encap_header_t *) vlib_buffer_get_current (b0)) - 1;
+ pvti_rx_peer_t *pvti_rx_peer0 =
+ pvti_try_find_or_create_rx_peer (ptd, b0, is_ip6);
+ if (!pvti_rx_peer0)
+ {
+ b0->error = node->errors[PVTI_INPUT_ERROR_PEER];
+ goto drop_and_maybe_trace;
+ }
+
+ b0 = vlib_get_buffer (vm, bi0);
+ pvti_packet_header_t *pvti0 = vlib_buffer_get_current (b0);
+ u8 stream_index = pvti0->stream_index;
+ max_chunk_count =
+ pvti0->chunk_count < MAX_CHUNKS ? pvti0->chunk_count : MAX_CHUNKS;
+ u16 pvti_packet_header_sz0 =
+ pvti0->pad_bytes + offsetof (pvti_packet_header_t, pad);
+ if (b0->current_length < pvti_packet_header_sz0)
+ {
+ b0->error = node->errors[PVTI_INPUT_ERROR_PACKET_TOO_SHORT];
+ goto drop_and_maybe_trace;
+ }
+ vlib_buffer_advance (b0, pvti_packet_header_sz0);
+
+ if (max_chunk_count == 0)
+ {
+ b0->error = node->errors[PVTI_INPUT_ERROR_NOCHUNKS];
+ goto drop_and_maybe_trace;
+ }
+ if (pvti0->reass_chunk_count > max_chunk_count)
+ {
+ b0->error = node->errors[PVTI_INPUT_ERROR_TOOMANYREASS];
+ goto drop_and_maybe_trace;
+ }
+ pvti_per_rx_stream_data_t *rx_stream0 =
+ &pvti_rx_peer0->rx_streams[stream_index];
+
+ u32 new_seq0 = clib_net_to_host_u32 (pvti0->seq);
+ if (new_seq0 == rx_stream0->last_rx_seq + 1)
+ {
+ /* Sequence# matches, we can attempt adding the leading chunks to
+ * reassembly */
+ rx_stream0->last_rx_seq = new_seq0;
+
+ while ((b0->current_length > 0) &&
+ true_chunk_count < pvti0->reass_chunk_count)
+ {
+ /* attempt to either incorporate the first chunk into
+ * reassembly or skip it. */
+ pvti_chunk_header_t *pvc0 = vlib_buffer_get_current (b0);
+ const u16 chunk_payload_length =
+ clib_net_to_host_u16 (pvc0->total_chunk_length) -
+ sizeof (*pvc0);
+ vlib_buffer_advance (b0, sizeof (*pvc0));
+
+ if (rx_stream0->rx_bi0 == INDEX_INVALID)
+ {
+ clib_warning (
+ "RX internal error: not-first chunk but no wip block");
+ }
+ else
+ {
+
+ vlib_buffer_t *rb0 =
+ vlib_get_buffer (vm, rx_stream0->rx_bi0);
+ u16 allowed_length =
+ PVTI_RX_MAX_LENGTH - rb0->current_length;
+ if (allowed_length > chunk_payload_length)
+ {
+ // simple case - there is space in the buffer to fit
+ // the whole chunk
+ void *tail =
+ vlib_buffer_put_uninit (rb0, chunk_payload_length);
+ clib_memcpy (tail, vlib_buffer_get_current (b0),
+ chunk_payload_length);
+ }
+ else
+ {
+ // The current chunk can not fit - need to make two
+ // copies, one into the current buffer, and one into
+ // a newly allocated chained buffer.
+ void *tail =
+ vlib_buffer_put_uninit (rb0, allowed_length);
+ clib_memcpy (tail, vlib_buffer_get_current (b0),
+ allowed_length);
+ u16 remaining_payload_length =
+ chunk_payload_length - allowed_length;
+ u32 nrbi0 = pvti_get_new_buffer (vm);
+ if (INDEX_INVALID == nrbi0)
+ {
+ ASSERT (0); // FIXME what the recovery is
+ // supposed to look like ?
+ }
+ else
+ {
+ // link up the new buffer and copy the remainder
+ // there
+ vlib_buffer_t *nrb0 = vlib_get_buffer (vm, nrbi0);
+ rb0->flags |= VLIB_BUFFER_NEXT_PRESENT;
+ rb0->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
+ rb0->next_buffer = nrbi0;
+ rx_stream0->rx_bi0 = nrbi0;
+ void *tail = vlib_buffer_put_uninit (
+ nrb0, remaining_payload_length);
+ clib_memcpy (tail,
+ vlib_buffer_get_current (b0) +
+ allowed_length,
+ remaining_payload_length);
+ }
+ }
+ pvti_rx_peer0->rx_streams[stream_index]
+ .rx_received_inner_length += chunk_payload_length;
+ if (pvti_rx_peer0->rx_streams[stream_index]
+ .rx_received_inner_length ==
+ pvti_rx_peer0->rx_streams[stream_index]
+ .rx_expected_inner_length)
+ {
+ next0 = rx_stream0->rx_next0;
+ pvti_enqueue_rx_bi_to_next_and_trace (
+ vm, node, ptd, rx_stream0->rx_bi0_first, next0);
+ pkts_decapsulated += 1;
+
+ // clean out the current reassemly state
+ rx_stream0->rx_bi0 = INDEX_INVALID;
+ rx_stream0->rx_bi0_first = INDEX_INVALID;
+ pvti_rx_peer0->rx_streams[stream_index]
+ .rx_received_inner_length = 0;
+ pvti_rx_peer0->rx_streams[stream_index]
+ .rx_expected_inner_length = 0;
+ rx_stream0->rx_next0 = 0;
+ }
+ }
+ chunks[true_chunk_count] = pvc0;
+ true_chunk_count += 1;
+ vlib_buffer_advance (b0, chunk_payload_length);
+ }
+ }
+ else
+ {
+ /* Sequence does not match, skip the reassembly chunks and reset
+ * the reassembly state */
+
+ while ((b0->current_length > 0) &&
+ true_chunk_count < pvti0->reass_chunk_count)
+ {
+ /* skip the reassembly chunks */
+ pvti_chunk_header_t *pvc0 = vlib_buffer_get_current (b0);
+ chunks[true_chunk_count] = pvc0;
+ true_chunk_count += 1;
+ vlib_buffer_advance (
+ b0, clib_net_to_host_u16 (pvc0->total_chunk_length));
+ }
+ // FIXME: discard the current reassembly state, reset the seq#
+ if (rx_stream0->rx_bi0_first != INDEX_INVALID)
+ {
+ clib_warning ("RX PVTI: discard chunk being reassembled");
+ vlib_buffer_free_one (vm, rx_stream0->rx_bi0_first);
+ rx_stream0->rx_bi0 = INDEX_INVALID;
+ rx_stream0->rx_bi0_first = INDEX_INVALID;
+ rx_stream0->rx_received_inner_length = 0;
+ rx_stream0->rx_expected_inner_length = 0;
+ rx_stream0->rx_next0 = 0;
+ }
+ }
+
+ while ((b0->current_length > 0) && true_chunk_count < max_chunk_count)
+ {
+ if (b0->current_length < sizeof (pvti_chunk_header_t))
+ {
+ clib_warning ("RX ERR: length too short for a chunk");
+ break;
+ }
+ pvti_chunk_header_t *pvc0 = vlib_buffer_get_current (b0);
+ chunks[true_chunk_count] = pvc0;
+ true_chunk_count += 1;
+ u16 total_chunk_length =
+ clib_net_to_host_u16 (pvc0->total_chunk_length);
+ if (b0->current_length < total_chunk_length)
+ {
+ clib_warning ("RX ERR: length 0x%x too big for a chunk",
+ true_chunk_count);
+ break;
+ }
+ u8 *pkt = (u8 *) (pvc0 + 1);
+ u16 inner_length;
+ if (rx_stream0->rx_bi0_first != INDEX_INVALID)
+ {
+ vlib_buffer_free_one (vm, rx_stream0->rx_bi0_first);
+ rx_stream0->rx_bi0 = INDEX_INVALID;
+ rx_stream0->rx_bi0_first = INDEX_INVALID;
+ rx_stream0->rx_received_inner_length = 0;
+ rx_stream0->rx_expected_inner_length = 0;
+ rx_stream0->rx_next0 = 0;
+ }
+
+ switch (*pkt & 0xf0)
+ {
+ case 0x40:
+ next0 = PVTI_INPUT_NEXT_IP4_INPUT;
+ inner_length = clib_net_to_host_u16 (*((u16 *) (pkt + 2)));
+ break;
+ case 0x60:
+ next0 = PVTI_INPUT_NEXT_IP6_INPUT;
+ inner_length = clib_net_to_host_u16 (*((u16 *) (pkt + 4))) +
+ sizeof (ip6_header_t);
+ break;
+ default:
+ next0 = PVTI_INPUT_NEXT_DROP;
+ vlib_buffer_advance (b0, total_chunk_length);
+ continue;
+ }
+ vlib_buffer_advance (b0, sizeof (pvti_chunk_header_t));
+
+ if (inner_length + sizeof (pvti_chunk_header_t) > total_chunk_length)
+ {
+ /* FIXME: the packet size is larger than the chunk -> it's a
+ * first fragment */
+ // enqueue the chunk and finish packet processing.
+ // There must be no active reassembly.
+ ASSERT (rx_stream0->rx_bi0_first == INDEX_INVALID);
+ rx_stream0->rx_next0 = next0;
+ rx_stream0->rx_bi0 = bi0;
+ rx_stream0->rx_bi0_first = bi0;
+ rx_stream0->rx_expected_inner_length = inner_length;
+ rx_stream0->rx_received_inner_length =
+ total_chunk_length - sizeof (pvti_chunk_header_t);
+ rx_stream0->last_rx_seq = new_seq0;
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ pvti_input_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = ~0;
+ t->trace_type = PVTI_INPUT_TRACE_enqueue;
+ clib_memcpy (t->packet_data, vlib_buffer_get_current (b0),
+ sizeof (t->packet_data));
+ }
+ goto continue_outer;
+ }
+
+ u32 nbi0 = pvti_get_new_buffer (vm);
+ if (INDEX_INVALID == nbi0)
+ {
+ decap_failed_no_buffers += 1;
+ continue;
+ };
+ vlib_buffer_t *nb0 = vlib_get_buffer (vm, nbi0);
+ pvti_if_t *pvti_if0 = pvti_if_get (pvti_rx_peer0->pvti_if_index);
+ vnet_buffer (nb0)->sw_if_index[VLIB_RX] = pvti_if0->sw_if_index;
+ void *new_packet = vlib_buffer_put_uninit (nb0, inner_length);
+ clib_memcpy (new_packet, pvc0 + 1, inner_length);
+ vlib_buffer_advance (b0, inner_length);
+
+ pvti_enqueue_rx_bi_to_next_and_trace (vm, node, ptd, nbi0, next0);
+ pkts_decapsulated += 1;
+ }
+ /* we have processed all the chunks from the buffer, but the buffer
+ * remains. Free it. */
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ pvti_input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = ~0;
+ t->trace_type = PVTI_INPUT_TRACE_free;
+ t->seq = clib_net_to_host_u32 (pvti0->seq);
+ t->chunk_count = pvti0->chunk_count;
+ u8 chunk_count =
+ pvti0->chunk_count < MAX_CHUNKS ? pvti0->chunk_count : MAX_CHUNKS;
+ for (int i = 0; i < chunk_count; i++)
+ {
+ t->chunks[i].total_chunk_length =
+ clib_net_to_host_u16 (chunks[i]->total_chunk_length);
+ }
+ clib_memcpy (t->packet_data, vlib_buffer_get_current (b0),
+ sizeof (t->packet_data));
+ }
+ vlib_buffer_free_one (vm, bi0);
+
+ continue_outer:
+ pkts_processed += 1;
+ continue;
+
+ drop_and_maybe_trace:
+ next0 = PVTI_INPUT_NEXT_DROP;
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ int i;
+ pvti_input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->trace_type = PVTI_INPUT_TRACE_drop;
+ t->next_index = next0;
+ t->remote_ip.ip.ip4 = h0->ip4.src_address;
+ t->remote_ip.version = AF_IP4;
+ t->local_port = h0->udp.dst_port;
+ t->remote_port = h0->udp.src_port;
+ if (!pvti_rx_peer0)
+ {
+ t->seq = 0xdeaddead;
+ }
+ else
+ {
+ t->seq = clib_net_to_host_u32 (pvti0->seq);
+ t->chunk_count = pvti0->chunk_count;
+ u8 chunk_count = pvti0->chunk_count < MAX_CHUNKS ?
+ pvti0->chunk_count :
+ MAX_CHUNKS;
+ for (i = 0; i < chunk_count; i++)
+ {
+ t->chunks[i].total_chunk_length =
+ clib_net_to_host_u16 (chunks[i]->total_chunk_length);
+ }
+ }
+ }
+
+ pkts_processed += 1;
+ vec_add1 (ptd->pending_rx_buffers, bi0);
+ vec_add1 (ptd->pending_rx_nexts, next0);
+ }
+
+ vlib_buffer_enqueue_to_next_vec (vm, node, &ptd->pending_rx_buffers,
+ &ptd->pending_rx_nexts,
+ vec_len (ptd->pending_rx_nexts));
+ vec_reset_length (ptd->pending_rx_buffers);
+ vec_reset_length (ptd->pending_rx_nexts);
+
+ vlib_node_increment_counter (vm, node->node_index,
+ PVTI_INPUT_ERROR_PROCESSED, pkts_processed);
+ vlib_node_increment_counter (
+ vm, node->node_index, PVTI_INPUT_ERROR_DECAPSULATED, pkts_decapsulated);
+ vlib_node_increment_counter (vm, node->node_index,
+ PVTI_INPUT_ERROR_NO_BUFFERS,
+ decap_failed_no_buffers);
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (pvti4_input_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return pvti_input_node_common (vm, node, frame, 0);
+}
+
+VLIB_NODE_FN (pvti6_input_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return pvti_input_node_common (vm, node, frame, 1);
+}
diff --git a/src/plugins/pvti/input.h b/src/plugins/pvti/input.h
new file mode 100644
index 00000000000..02a186cde05
--- /dev/null
+++ b/src/plugins/pvti/input.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_pvti_input_h__
+#define __included_pvti_input_h__
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+
+typedef struct
+{
+ u16 total_chunk_length;
+} pvti_input_chunk_t;
+
+#define MAX_CHUNKS 32
+#define PVTI_RX_MAX_LENGTH 2048
+
+typedef struct
+{
+ u32 next_index;
+ u32 sw_if_index;
+ ip_address_t remote_ip;
+ u16 remote_port;
+ u16 local_port;
+ u32 seq;
+ pvti_input_chunk_t chunks[MAX_CHUNKS];
+ u8 chunk_count;
+ u8 trace_type;
+ u8 packet_data[64];
+} pvti_input_trace_t;
+
+#define foreach_pvti_input_trace_type \
+ _ (drop, "drop") \
+ _ (decap, "decapsulate") \
+ _ (free, "free") \
+ _ (enqueue, "enqueue")
+
+typedef enum
+{
+#define _(f, s) PVTI_INPUT_TRACE_##f,
+ foreach_pvti_input_trace_type
+#undef _
+ PVTI_INPUT_TRACE_N_TYPES,
+} pvti_input_trace_type_t;
+
+#define foreach_pvti_input_error \
+ _ (PROCESSED, "PVTI tunneled packets processed") \
+ _ (DECAPSULATED, "PVTI inner packets decapsulated") \
+ _ (PEER, "Could not find a peer") \
+ _ (NOCHUNKS, "Packet has no chunks") \
+ _ (NO_BUFFERS, "No buffers available to decapsulate") \
+ _ (TOOMANYREASS, "Packet has more reassembly chunks than total") \
+ _ (PACKET_TOO_SHORT, "Packet too short")
+
+typedef enum
+{
+#define _(sym, str) PVTI_INPUT_ERROR_##sym,
+ foreach_pvti_input_error
+#undef _
+ PVTI_INPUT_N_ERROR,
+} pvti_input_error_t;
+
+typedef enum
+{
+ PVTI_INPUT_NEXT_DROP,
+ PVTI_INPUT_NEXT_IP4_INPUT,
+ PVTI_INPUT_NEXT_IP6_INPUT,
+ PVTI_INPUT_NEXT_PUNT,
+ PVTI_INPUT_N_NEXT,
+} pvti_input_next_t;
+
+#endif // pvti_input_h
diff --git a/src/plugins/pvti/output-main.c b/src/plugins/pvti/output-main.c
new file mode 100644
index 00000000000..ae4ae5f8e98
--- /dev/null
+++ b/src/plugins/pvti/output-main.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pvti/output.h>
+
+/* packet trace format function */
+static u8 *
+format_pvti_output_trace (u8 *s, va_list *args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ pvti_output_trace_t *t = va_arg (*args, pvti_output_trace_t *);
+
+ u32 indent = format_get_indent (s);
+ s =
+ format (s, "PVTI-OUT(%d): sw_if_index %d, next index %d, underlay_mtu %d,",
+ t->trace_type, t->sw_if_index, t->next_index, t->underlay_mtu);
+ s = format (s, "\n%U stream_index %d, bi0_max_current_length %d, tx_seq %d",
+ format_white_space, indent, t->stream_index,
+ t->bi0_max_current_length, t->tx_seq);
+ s = format (s, "\n%U%U", format_white_space, indent,
+ format_ip_adjacency_packet_data, t->packet_data,
+ sizeof (t->packet_data));
+
+ return s;
+}
+
+vlib_node_registration_t pvti_output_node;
+
+static char *pvti_output_error_strings[] = {
+#define _(sym, string) string,
+ foreach_pvti_output_error
+#undef _
+};
+
+VLIB_REGISTER_NODE (pvti4_output_node) =
+{
+ .name = "pvti4-output",
+ .vector_size = sizeof (u32),
+ .format_trace = format_pvti_output_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN(pvti_output_error_strings),
+ .error_strings = pvti_output_error_strings,
+
+ .n_next_nodes = PVTI_OUTPUT_N_NEXT,
+
+ .next_nodes = {
+ [PVTI_OUTPUT_NEXT_DROP] = "error-drop",
+ [PVTI_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx",
+ [PVTI_OUTPUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [PVTI_OUTPUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ },
+
+};
+VLIB_REGISTER_NODE (pvti6_output_node) =
+{
+ .name = "pvti6-output",
+ .vector_size = sizeof (u32),
+ .format_trace = format_pvti_output_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN(pvti_output_error_strings),
+ .error_strings = pvti_output_error_strings,
+
+ .n_next_nodes = PVTI_OUTPUT_N_NEXT,
+
+ .next_nodes = {
+ [PVTI_OUTPUT_NEXT_DROP] = "error-drop",
+ [PVTI_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx",
+ [PVTI_OUTPUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [PVTI_OUTPUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ },
+
+};
diff --git a/src/plugins/pvti/output.c b/src/plugins/pvti/output.c
new file mode 100644
index 00000000000..1939c6f585a
--- /dev/null
+++ b/src/plugins/pvti/output.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+#include <pvti/output.h>
+
+static_always_inline u32
+ip6_vtcfl (u8 stream_index)
+{
+ u32 vtcfl = 0x6 << 28;
+ vtcfl |= stream_index;
+
+ return (clib_host_to_net_u32 (vtcfl));
+}
+
+always_inline vlib_buffer_t *
+pvti_alloc_new_tx_buffer (vlib_main_t *vm)
+{
+ u32 bi0 = INDEX_INVALID;
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ {
+ return 0;
+ }
+ vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
+ b0->current_data = 0;
+ b0->current_length = 0;
+ return b0;
+}
+
+always_inline bool
+pvti_find_or_try_create_tx_peer (vlib_main_t *vm, pvti_per_thread_data_t *ptd,
+ pvti_if_t *pvti_if0, ip_address_t *remote_ip,
+ u16 remote_port, u32 *out_index)
+{
+
+ pvti_tx_peer_t *peer;
+ pool_foreach (peer, ptd->tx_peers)
+ {
+ if (peer->remote_port == remote_port &&
+ 0 == ip_address_cmp (remote_ip, &peer->remote_ip))
+ {
+ if (peer->deleted)
+ {
+ // Bad luck, the peer has been deleted.
+ u32 boi0 = vlib_get_buffer_index (vm, peer->bo0);
+ if (peer->bo0)
+ {
+ vlib_buffer_free (vm, &boi0, 1);
+ }
+ clib_memset (peer, 0xca, sizeof (*peer));
+ pool_put (ptd->tx_peers, peer);
+ continue;
+ }
+ *out_index = peer - ptd->tx_peers;
+ return 1;
+ }
+ }
+
+ ip_address_family_t dst_ver = ip_addr_version (&pvti_if0->remote_ip);
+
+ u16 pvti_encap_overhead = (dst_ver == AF_IP6) ?
+ sizeof (pvti_ip6_encap_header_t) :
+ sizeof (pvti_ip4_encap_header_t);
+
+ u16 pvti_packet_overhead =
+ pvti_encap_overhead + sizeof (pvti_packet_header_t) + PVTI_ALIGN_BYTES;
+
+ ASSERT (pvti_if0->underlay_mtu > pvti_packet_overhead);
+
+ u32 bo0_max_current_length = pvti_if0->underlay_mtu - pvti_packet_overhead;
+
+ vlib_buffer_t *bo0 = pvti_alloc_new_tx_buffer (vm);
+
+ if (!bo0)
+ {
+ return 0;
+ }
+
+ pvti_tx_peer_t new_peer = {
+ .local_ip = pvti_if0->local_ip,
+ .remote_ip = *remote_ip,
+ .local_port = pvti_if0->local_port,
+ .remote_port = remote_port,
+ .underlay_mtu = pvti_if0->underlay_mtu,
+ .underlay_fib_index = pvti_if0->underlay_fib_index,
+ .bo0_max_current_length = bo0_max_current_length,
+ .pvti_if_index = pvti_if_get_index (pvti_if0),
+ .deleted = 0,
+ .bo0 = bo0,
+ .chunk_count = 0,
+ .reass_chunk_count = 0,
+ .current_tx_seq = 42,
+ };
+
+ pvti_tx_peer_t *tx_new_peer;
+ pool_get (ptd->tx_peers, tx_new_peer);
+
+ *tx_new_peer = new_peer;
+ *out_index = tx_new_peer - ptd->tx_peers;
+ return 1;
+}
+
+always_inline bool
+pvti_try_get_tx_peer_index (vlib_main_t *vm, pvti_per_thread_data_t *ptd,
+ pvti_if_t *pvti_if0, vlib_buffer_t *b0,
+ bool is_ip6, u32 *out_index)
+{
+ if (pvti_if0->peer_address_from_payload)
+ {
+ ip_address_t remote_ip = { 0 };
+ if (is_ip6)
+ {
+ ip6_header_t *ip6 = vlib_buffer_get_current (b0);
+ ip_address_set (&remote_ip, &ip6->dst_address, AF_IP6);
+ }
+ else
+ {
+ ip4_header_t *ip4 = vlib_buffer_get_current (b0);
+ ip_address_set (&remote_ip, &ip4->dst_address, AF_IP4);
+ }
+ return pvti_find_or_try_create_tx_peer (
+ vm, ptd, pvti_if0, &remote_ip, pvti_if0->remote_port, out_index);
+ }
+ else
+ {
+ return pvti_find_or_try_create_tx_peer (
+ vm, ptd, pvti_if0, &pvti_if0->remote_ip, pvti_if0->remote_port,
+ out_index);
+ }
+ /* not reached */
+}
+
+always_inline void
+pvti_finalize_chunk (pvti_tx_peer_t *tx_peer,
+ pvti_chunk_header_t *chunk_header, u8 *tail,
+ bool is_reassembly_chunk)
+{
+ clib_memset (chunk_header, 0xab, sizeof (pvti_chunk_header_t));
+ chunk_header->total_chunk_length =
+ clib_host_to_net_u16 (tail - (u8 *) chunk_header);
+ tx_peer->chunk_count++;
+ if (is_reassembly_chunk)
+ {
+ tx_peer->reass_chunk_count++;
+ }
+}
+
+always_inline pvti_output_next_t
+encap_pvti_buffer_ip46 (vlib_main_t *vm, vlib_node_runtime_t *node,
+ pvti_tx_peer_t *tx_peer, int is_ip6)
+{
+ ip_address_family_t src_ver = ip_addr_version (&tx_peer->local_ip);
+ ip_address_family_t dst_ver = ip_addr_version (&tx_peer->remote_ip);
+ u8 stream_index = 0;
+
+ ASSERT (src_ver == dst_ver);
+ bool is_ip6_encap = (AF_IP6 == src_ver);
+
+ vlib_buffer_t *b0 = tx_peer->bo0;
+ vlib_buffer_advance (b0,
+ -(sizeof (pvti_packet_header_t) + PVTI_ALIGN_BYTES));
+
+ pvti_packet_header_t *pvti0 = vlib_buffer_get_current (b0);
+ clib_memset (pvti0, 0xca, sizeof (*pvti0) + PVTI_ALIGN_BYTES);
+ pvti0->pad_bytes = PVTI_ALIGN_BYTES;
+
+ pvti0->seq = clib_host_to_net_u32 (tx_peer->current_tx_seq);
+ pvti0->stream_index = stream_index;
+ pvti0->reass_chunk_count = tx_peer->reass_chunk_count;
+ pvti0->chunk_count = tx_peer->chunk_count;
+ pvti0->mandatory_flags_mask = 0;
+ pvti0->flags_value = 0;
+
+ if (is_ip6_encap)
+ {
+ vlib_buffer_advance (b0, -(sizeof (pvti_ip6_encap_header_t)));
+ if (b0->current_data < -VLIB_BUFFER_PRE_DATA_SIZE)
+ {
+ // undo the change
+ vlib_buffer_advance (b0, (sizeof (pvti_ip6_encap_header_t)));
+ b0->error = node->errors[PVTI_OUTPUT_ERROR_NO_PRE_SPACE];
+ return PVTI_OUTPUT_NEXT_DROP;
+ }
+ pvti_ip6_encap_header_t *ve = vlib_buffer_get_current (b0);
+
+ ve->udp.src_port = clib_host_to_net_u16 (tx_peer->local_port);
+ ve->udp.dst_port = clib_host_to_net_u16 (tx_peer->remote_port);
+ ve->udp.length = clib_host_to_net_u16 (
+ b0->current_length - offsetof (pvti_ip6_encap_header_t, udp));
+ ve->udp.checksum = 0;
+
+ ve->ip6.ip_version_traffic_class_and_flow_label =
+ ip6_vtcfl (stream_index);
+ ve->ip6.payload_length = ve->udp.length;
+ ve->ip6.protocol = 17;
+ ve->ip6.hop_limit = 128;
+ ip_address_copy_addr (&ve->ip6.src_address, &tx_peer->local_ip);
+ ip_address_copy_addr (&ve->ip6.dst_address, &tx_peer->remote_ip);
+ }
+ else
+ {
+ vlib_buffer_advance (b0, -(sizeof (pvti_ip4_encap_header_t)));
+ if (b0->current_data < -VLIB_BUFFER_PRE_DATA_SIZE)
+ {
+ // undo the change
+ vlib_buffer_advance (b0, (sizeof (pvti_ip4_encap_header_t)));
+ b0->error = node->errors[PVTI_OUTPUT_ERROR_NO_PRE_SPACE];
+ return PVTI_OUTPUT_NEXT_DROP;
+ }
+ pvti_ip4_encap_header_t *ve = vlib_buffer_get_current (b0);
+
+ ve->udp.src_port = clib_host_to_net_u16 (tx_peer->local_port);
+ ve->udp.dst_port = clib_host_to_net_u16 (tx_peer->remote_port);
+ ve->udp.length = clib_host_to_net_u16 (
+ b0->current_length - offsetof (pvti_ip4_encap_header_t, udp));
+ ve->udp.checksum = 0;
+
+ ve->ip4.ip_version_and_header_length = 0x45;
+ ve->ip4.tos = 0;
+ ve->ip4.length = clib_host_to_net_u16 (b0->current_length);
+ ve->ip4.fragment_id =
+ clib_host_to_net_u16 (tx_peer->current_tx_seq & 0xffff);
+ ve->ip4.flags_and_fragment_offset = 0;
+ ve->ip4.ttl = 128;
+ ve->ip4.protocol = 17;
+
+ ve->ip4.dst_address.as_u32 = ip_addr_v4 (&tx_peer->remote_ip).data_u32;
+ ve->ip4.src_address.as_u32 = ip_addr_v4 (&tx_peer->local_ip).data_u32;
+ ve->ip4.checksum = ip4_header_checksum (&ve->ip4);
+ }
+
+ // This is important, if not reset, causes a crash
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = tx_peer->underlay_fib_index;
+
+ // vnet_buffer (b0)->oflags |= VNET_BUFFER_OFFLOAD_F_IP_CKSUM;
+ return is_ip6_encap ? PVTI_OUTPUT_NEXT_IP6_LOOKUP :
+ PVTI_OUTPUT_NEXT_IP4_LOOKUP;
+}
+
+always_inline void
+pvti_enqueue_tx_and_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
+ pvti_per_thread_data_t *ptd, vlib_buffer_t *b0,
+ u16 next0, u8 stream_index, pvti_tx_peer_t *tx_peer)
+{
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ tx_peer->is_bo0_traced))
+ {
+ if (PREDICT_TRUE (
+ vlib_trace_buffer (vm, node, next0, b0, /* follow_chain */ 0)))
+ {
+
+ pvti_output_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ t->underlay_mtu = tx_peer->underlay_mtu;
+ t->stream_index = stream_index;
+ t->trace_type = 1;
+ t->bi0_max_current_length = tx_peer->bo0_max_current_length;
+ t->tx_seq = tx_peer->current_tx_seq;
+ clib_memcpy (t->packet_data, vlib_buffer_get_current (b0),
+ sizeof (t->packet_data));
+ }
+ }
+ u32 bi0 = vlib_get_buffer_index (vm, b0);
+ vec_add1 (ptd->pending_tx_buffers, bi0);
+ vec_add1 (ptd->pending_tx_nexts, next0);
+}
+
+always_inline void
+pvti_enqueue_tx_drop_and_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
+ pvti_per_thread_data_t *ptd, vlib_buffer_t *b0,
+ u8 stream_index)
+{
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ pvti_output_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = PVTI_OUTPUT_NEXT_DROP;
+ t->stream_index = stream_index;
+ t->trace_type = 0;
+ clib_memcpy (t->packet_data, vlib_buffer_get_current (b0),
+ sizeof (t->packet_data));
+ }
+ u32 bi0 = vlib_get_buffer_index (vm, b0);
+ vec_add1 (ptd->pending_tx_buffers, bi0);
+ vec_add1 (ptd->pending_tx_nexts, PVTI_OUTPUT_NEXT_DROP);
+}
+
+always_inline bool
+pvti_flush_peer_and_recharge (vlib_main_t *vm, vlib_node_runtime_t *node,
+ pvti_per_thread_data_t *ptd, u32 tx_peer_index,
+ u8 stream_index, const bool is_ip6)
+{
+ pvti_tx_peer_t *tx_peer = pool_elt_at_index (ptd->tx_peers, tx_peer_index);
+ u16 next0 = encap_pvti_buffer_ip46 (vm, node, tx_peer, is_ip6);
+
+ pvti_enqueue_tx_and_trace (vm, node, ptd, tx_peer->bo0, next0, stream_index,
+ tx_peer);
+
+ tx_peer->bo0 = pvti_alloc_new_tx_buffer (vm);
+ tx_peer->reass_chunk_count = 0;
+ tx_peer->chunk_count = 0;
+ tx_peer->current_tx_seq++;
+
+ return 1;
+}
+
+always_inline u16
+pvti_output_node_common (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, const bool is_ip6)
+{
+ pvti_main_t *pvm = &pvti_main;
+
+ u32 n_left_from, *from;
+ u32 pkts_encapsulated = 0;
+ u32 pkts_processed = 0;
+ u32 pkts_chopped = 0;
+ u32 pkts_overflow = 0;
+ u32 pkts_overflow_cantfit = 0;
+
+ bool is_node_traced = (node->flags & VLIB_NODE_FLAG_TRACE) ? 1 : 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+
+ u8 stream_index = pvti_get_stream_index (is_ip6);
+
+ u32 thread_index = vlib_get_thread_index ();
+ pvti_per_thread_data_t *ptd =
+ vec_elt_at_index (pvm->per_thread_data[is_ip6], thread_index);
+
+ vlib_buffer_t *ibufs[VLIB_FRAME_SIZE], **ib = ibufs;
+
+ vlib_get_buffers (vm, from, ibufs, n_left_from);
+
+ n_left_from = frame->n_vectors;
+ while (1 && n_left_from > 0)
+ {
+ n_left_from -= 1;
+ vlib_buffer_t *b0 = ib[0];
+ ib++;
+ u32 bi0 = vlib_get_buffer_index (vm, b0);
+ bool is_b0_traced =
+ is_node_traced && ((b0->flags & VLIB_BUFFER_IS_TRACED) ? 1 : 0);
+ pkts_processed += 1;
+
+ u32 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ u32 pvti_index0 = pvti_if_find_by_sw_if_index (sw_if_index0);
+ if (pvti_index0 == INDEX_INVALID)
+ {
+ b0->error = node->errors[PVTI_OUTPUT_ERROR_PEER];
+ pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0, stream_index);
+ continue;
+ }
+ pvti_if_t *pvti_if0 = pvti_if_get (pvti_index0);
+ u32 tx_peer_index;
+ if (!pvti_try_get_tx_peer_index (vm, ptd, pvti_if0, b0, is_ip6,
+ &tx_peer_index))
+ {
+ b0->error = node->errors[PVTI_OUTPUT_ERROR_MAKE_PEER];
+ pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0, stream_index);
+ continue;
+ }
+ pvti_tx_peer_t *tx_peer = &ptd->tx_peers[tx_peer_index];
+
+ u32 b0_len = vlib_buffer_length_in_chain (vm, b0);
+ u32 total_chunk_len = sizeof (pvti_chunk_header_t) + b0_len;
+
+ if (tx_peer->bo0_max_current_length >=
+ tx_peer->bo0->current_length + total_chunk_len)
+ {
+ /* Happy case, we can fit the entire new chunk */
+ pvti_chunk_header_t *chunk_header = vlib_buffer_put_uninit (
+ tx_peer->bo0, sizeof (pvti_chunk_header_t));
+ u8 *tail = vlib_buffer_put_uninit (tx_peer->bo0, b0_len);
+ vlib_buffer_t *b0_curr;
+ b0_curr = b0;
+ while (b0_len > 0)
+ {
+ clib_memcpy (tail, vlib_buffer_get_current (b0_curr),
+ b0_curr->current_length);
+ tail += b0_curr->current_length;
+ b0_len -= b0_curr->current_length;
+ ASSERT ((b0_len == 0) ||
+ (b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT));
+ if (b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT)
+ {
+ b0_curr = vlib_get_buffer (vm, b0_curr->next_buffer);
+ }
+ }
+ tx_peer->is_bo0_traced |= is_b0_traced;
+ pvti_finalize_chunk (tx_peer, chunk_header, tail, false);
+ }
+ else
+ {
+ bool is_reassembly = false;
+ /* FIXME: here, flush a packet if we want to avoid fragmenting it */
+#define PVTI_TINY_PACKET_SZ 20
+ int threshold_len =
+ sizeof (pvti_chunk_header_t) + PVTI_TINY_PACKET_SZ;
+
+ /* Can we fit anything meaningful into bo0 ? if not - flush */
+ if (tx_peer->bo0_max_current_length <=
+ tx_peer->bo0->current_length + threshold_len)
+ {
+ if (!pvti_flush_peer_and_recharge (vm, node, ptd, tx_peer_index,
+ stream_index, is_ip6))
+ {
+ b0->error = node->errors[PVTI_OUTPUT_ERROR_RECHARGE0];
+ pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0,
+ stream_index);
+ continue;
+ }
+ pkts_encapsulated += 1;
+ }
+
+ pvti_chunk_header_t *chunk_header = vlib_buffer_put_uninit (
+ tx_peer->bo0, sizeof (pvti_chunk_header_t));
+
+ u8 *tail;
+ vlib_buffer_t *b0_curr;
+ /* append the chained buffers and flush as necessary */
+ b0_curr = b0;
+
+ int curr_b0_start_offset = 0;
+
+ while (b0_len > 0)
+ {
+ ASSERT (tx_peer->bo0_max_current_length >
+ tx_peer->bo0->current_length);
+ int copy_len =
+ clib_min (b0_curr->current_length - curr_b0_start_offset,
+ tx_peer->bo0_max_current_length -
+ tx_peer->bo0->current_length);
+ tail = vlib_buffer_put_uninit (tx_peer->bo0, copy_len);
+ clib_memcpy (tail,
+ (u8 *) vlib_buffer_get_current (b0_curr) +
+ curr_b0_start_offset,
+ copy_len);
+ tail += copy_len;
+ b0_len -= copy_len;
+ // Advance the start offset or reset it if we copied the entire
+ // block
+ curr_b0_start_offset =
+ curr_b0_start_offset + copy_len == b0_curr->current_length ?
+ 0 :
+ curr_b0_start_offset + copy_len;
+ ASSERT ((b0_len == 0) || (curr_b0_start_offset > 0) ||
+ (b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT));
+ if (curr_b0_start_offset > 0)
+ {
+ pvti_finalize_chunk (tx_peer, chunk_header, tail,
+ is_reassembly);
+ tx_peer->is_bo0_traced |= is_b0_traced;
+ if (!pvti_flush_peer_and_recharge (
+ vm, node, ptd, tx_peer_index, stream_index, is_ip6))
+ {
+ b0->error = node->errors[PVTI_OUTPUT_ERROR_RECHARGE1];
+ pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0,
+ stream_index);
+ continue;
+ }
+ pkts_encapsulated += 1;
+ /* next chunk(s) will be reassembly until the next block */
+ is_reassembly = true;
+ chunk_header = vlib_buffer_put_uninit (
+ tx_peer->bo0, sizeof (pvti_chunk_header_t));
+ }
+ else
+ {
+ if ((b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT))
+ {
+ b0_curr = vlib_get_buffer (vm, b0_curr->next_buffer);
+ }
+ else
+ {
+ pvti_finalize_chunk (tx_peer, chunk_header, tail,
+ is_reassembly);
+ tx_peer->is_bo0_traced |= is_b0_traced;
+ }
+ }
+ }
+ }
+ vlib_buffer_free_one (vm, bi0);
+ }
+
+ int i;
+ for (i = 0; i < vec_len (ptd->tx_peers); i++)
+ {
+ if (ptd->tx_peers[i].chunk_count)
+ {
+ pvti_flush_peer_and_recharge (vm, node, ptd, i, stream_index,
+ is_ip6);
+ pkts_encapsulated += 1;
+ }
+ }
+
+ vlib_buffer_enqueue_to_next_vec (vm, node, &ptd->pending_tx_buffers,
+ &ptd->pending_tx_nexts,
+ vec_len (ptd->pending_tx_nexts));
+ vec_reset_length (ptd->pending_tx_buffers);
+ vec_reset_length (ptd->pending_tx_nexts);
+
+ vlib_node_increment_counter (
+ vm, node->node_index, PVTI_OUTPUT_ERROR_ENCAPSULATED, pkts_encapsulated);
+ vlib_node_increment_counter (vm, node->node_index,
+ PVTI_OUTPUT_ERROR_PROCESSED, pkts_processed);
+ vlib_node_increment_counter (vm, node->node_index, PVTI_OUTPUT_ERROR_CHOPPED,
+ pkts_chopped);
+ vlib_node_increment_counter (vm, node->node_index,
+ PVTI_OUTPUT_ERROR_OVERFLOW, pkts_overflow);
+ vlib_node_increment_counter (vm, node->node_index,
+ PVTI_OUTPUT_ERROR_OVERFLOW_CANTFIT,
+ pkts_overflow_cantfit);
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (pvti4_output_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return pvti_output_node_common (vm, node, frame, 0);
+}
+
+VLIB_NODE_FN (pvti6_output_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return pvti_output_node_common (vm, node, frame, 1);
+}
diff --git a/src/plugins/pvti/output.h b/src/plugins/pvti/output.h
new file mode 100644
index 00000000000..95e78ba9720
--- /dev/null
+++ b/src/plugins/pvti/output.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_pvti_output_h__
+#define __included_pvti_output_h__
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+
+typedef struct
+{
+ u32 next_index;
+ u32 sw_if_index;
+ u32 tx_seq;
+ u16 underlay_mtu;
+ u16 bi0_max_current_length;
+ u8 stream_index;
+ u8 trace_type;
+ u8 packet_data[96];
+} pvti_output_trace_t;
+
+#define foreach_pvti_output_error \
+ _ (NONE, "No error") \
+ _ (PROCESSED, "Packets processed") \
+ _ (ENCAPSULATED, "Packets encapsulated") \
+ _ (PEER, "No peer found") \
+ _ (MAKE_PEER, "Could not make peer") \
+ _ (RECHARGE0, "Could not recharge 0") \
+ _ (RECHARGE1, "Could not recharge 1") \
+ _ (NO_PRE_SPACE, "Not enought pre-data space") \
+ _ (CHOPPED, "Packets chopped") \
+ _ (OVERFLOW, "Packets overflowed") \
+ _ (OVERFLOW_CANTFIT, "Packets overflowed and cant fit excess")
+
+typedef enum
+{
+#define _(sym, str) PVTI_OUTPUT_ERROR_##sym,
+ foreach_pvti_output_error
+#undef _
+ PVTI_OUTPUT_N_ERROR,
+} pvti_output_error_t;
+
+typedef enum
+{
+ PVTI_INDEPENDENT_CHUNK = 0,
+ PVTI_REASS_CHUNK,
+} pvti_chunk_type_t;
+
+#define MAX_CURR_LEN_UNKNOWN 0xffff
+
+typedef enum
+{
+ PVTI_OUTPUT_NEXT_DROP,
+ PVTI_OUTPUT_NEXT_INTERFACE_OUTPUT,
+ PVTI_OUTPUT_NEXT_IP4_LOOKUP,
+ PVTI_OUTPUT_NEXT_IP6_LOOKUP,
+ PVTI_OUTPUT_N_NEXT,
+} pvti_output_next_t;
+
+#endif // pvti_output_h
diff --git a/src/plugins/pvti/pvti.api b/src/plugins/pvti/pvti.api
new file mode 100644
index 00000000000..859ed1ab6b0
--- /dev/null
+++ b/src/plugins/pvti/pvti.api
@@ -0,0 +1,111 @@
+/*
+ * pvti.api - binary API skeleton
+ *
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file pvti.api
+ * @brief VPP control-plane API messages.
+ *
+ * This file defines VPP control-plane binary API messages which are generally
+ * called through a shared memory interface.
+ */
+
+/* Version and type recitations */
+
+option version = "0.0.1";
+import "vnet/interface_types.api";
+import "vnet/ip/ip_types.api";
+
+/** \brief A composite type uniquely defining a PVTI tunnel.
+ @param sw_if_index - ignored on create/delete, present in details.
+ @param src_ip - Source IP address
+ @param src_port - Source UDP port
+ @param dst_ip - Destination IP address
+ @param dst_port - Destination UDP port
+ @param underlay_mtu - Underlay MTU for packet splitting/coalescing
+ @param underlay_fib_index - Underlay FIB index to be used after encap
+*/
+typedef pvti_tunnel
+{
+ vl_api_interface_index_t sw_if_index;
+ vl_api_address_t local_ip;
+ u16 local_port;
+ vl_api_address_t remote_ip;
+ bool peer_address_from_payload;
+ u16 remote_port;
+ u16 underlay_mtu;
+ u32 underlay_fib_index;
+};
+
+
+/** @brief API to enable / disable pvti on an interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param enable_disable - 1 to enable, 0 to disable the feature
+ @param sw_if_index - interface handle
+*/
+
+define pvti_interface_create
+{
+ option status="in_progress";
+
+ /* Client identifier, set from api_main.my_client_index */
+ u32 client_index;
+
+ /* Arbitrary context, so client can match reply to request */
+ u32 context;
+ vl_api_pvti_tunnel_t interface;
+};
+
+define pvti_interface_create_reply
+{
+ option status="in_progress";
+ u32 context;
+ i32 retval;
+
+ /* Index for the newly created interface */
+ vl_api_interface_index_t sw_if_index;
+};
+
+autoreply define pvti_interface_delete {
+ option status="in_progress";
+
+ /* Client identifier, set from api_main.my_client_index */
+ u32 client_index;
+
+ /* Arbitrary context, so client can match reply to request */
+ u32 context;
+
+ vl_api_interface_index_t sw_if_index;
+};
+
+
+define pvti_interface_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+};
+
+define pvti_interface_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_pvti_tunnel_t interface;
+};
+
+
diff --git a/src/plugins/pvti/pvti.c b/src/plugins/pvti/pvti.c
new file mode 100644
index 00000000000..646276dec09
--- /dev/null
+++ b/src/plugins/pvti/pvti.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <vnet/fib/fib_table.h>
+#include <pvti/pvti.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vpp/app/version.h>
+#include <stdbool.h>
+
+#include <pvti/pvti.api_enum.h>
+#include <pvti/pvti.api_types.h>
+
+#include <pvti/pvti_if.h>
+
+#define REPLY_MSG_ID_BASE pmp->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+#include <vnet/ip/ip_format_fns.h>
+
+pvti_main_t pvti_main;
+
+u8 *
+format_pvti_tx_peer_ptr (u8 *s, va_list *args)
+{
+ pvti_tx_peer_t *peer = va_arg (*args, pvti_tx_peer_t *);
+
+ s = format (
+ s,
+ "[%p]%s local:%U:%d remote:%U:%d underlay_mtu:%d underlay_fib_idx:%d "
+ "pvti_idx:%d b0_max_clen:%d cseq:%d chunk_count:%d reass_chunk_count:%d",
+ peer, peer->deleted ? " DELETED" : "", format_ip46_address,
+ &peer->local_ip, IP46_TYPE_ANY, peer->local_port, format_ip46_address,
+ &peer->remote_ip, IP46_TYPE_ANY, peer->remote_port, peer->underlay_mtu,
+ peer->underlay_fib_index, peer->pvti_if_index,
+ peer->bo0_max_current_length, peer->current_tx_seq, peer->chunk_count,
+ peer->reass_chunk_count);
+
+ return (s);
+}
+
+u8 *
+format_pvti_rx_peer_ptr (u8 *s, va_list *args)
+{
+ pvti_rx_peer_t *peer = va_arg (*args, pvti_rx_peer_t *);
+
+ s = format (s, "[%p]%s local:%U:%d remote:%U:%d pvti_idx:%d", peer,
+ peer->deleted ? " DELETED" : "", format_ip46_address,
+ &peer->local_ip, IP46_TYPE_ANY, peer->local_port,
+ format_ip46_address, &peer->remote_ip, IP46_TYPE_ANY,
+ peer->remote_port, peer->pvti_if_index);
+
+ return (s);
+}
+
+void
+pvti_verify_initialized (pvti_main_t *pvm)
+{
+ if (!pvm->is_initialized)
+ {
+ const int n_threads = vlib_get_n_threads ();
+ vec_validate (pvm->per_thread_data[0], n_threads - 1);
+ vec_validate (pvm->per_thread_data[1], n_threads - 1);
+ pvm->is_initialized = 1;
+ }
+}
+
+void
+vnet_int_pvti_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
+{
+ pvti_main_t *pvm = &pvti_main;
+
+ if (pool_is_free_index (pvm->vnet_main->interface_main.sw_interfaces,
+ sw_if_index))
+ return;
+
+ pvti_verify_initialized (pvm);
+
+ is_enable = !!is_enable;
+
+ if (is_ip6)
+ {
+ if (clib_bitmap_get (pvm->bm_ip6_bypass_enabled_by_sw_if, sw_if_index) !=
+ is_enable)
+ {
+ vnet_feature_enable_disable ("ip6-unicast", "ip6-pvti-bypass",
+ sw_if_index, is_enable, 0, 0);
+ pvm->bm_ip6_bypass_enabled_by_sw_if = clib_bitmap_set (
+ pvm->bm_ip6_bypass_enabled_by_sw_if, sw_if_index, is_enable);
+ }
+ }
+ else
+ {
+ if (clib_bitmap_get (pvm->bm_ip4_bypass_enabled_by_sw_if, sw_if_index) !=
+ is_enable)
+ {
+ vnet_feature_enable_disable ("ip4-unicast", "ip4-pvti-bypass",
+ sw_if_index, is_enable, 0, 0);
+ pvm->bm_ip4_bypass_enabled_by_sw_if = clib_bitmap_set (
+ pvm->bm_ip4_bypass_enabled_by_sw_if, sw_if_index, is_enable);
+ }
+ }
+}
+
+static clib_error_t *
+set_ip_pvti_bypass (u32 is_ip6, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_main_t *vnm = vnet_get_main ();
+ clib_error_t *error = 0;
+ u32 sw_if_index, is_enable;
+
+ sw_if_index = ~0;
+ is_enable = 1;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat_user (line_input, unformat_vnet_sw_interface, vnm,
+ &sw_if_index))
+ ;
+ else if (unformat (line_input, "del"))
+ is_enable = 0;
+ else
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+ }
+
+ if (~0 == sw_if_index)
+ {
+ error = clib_error_return (0, "unknown interface `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+
+ vnet_int_pvti_bypass_mode (sw_if_index, is_ip6, is_enable);
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
+set_ip4_pvti_bypass (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ return set_ip_pvti_bypass (0, input, cmd);
+}
+
+VLIB_CLI_COMMAND (set_interface_ip_pvti_bypass_command, static) = {
+ .path = "set interface ip pvti-bypass",
+ .function = set_ip4_pvti_bypass,
+ .short_help = "set interface ip pvti-bypass <interface> [del]",
+};
+
+static clib_error_t *
+set_ip6_pvti_bypass (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ return set_ip_pvti_bypass (1, input, cmd);
+}
+
+VLIB_CLI_COMMAND (set_interface_ip6_pvti_bypass_command, static) = {
+ .path = "set interface ip6 pvti-bypass",
+ .function = set_ip6_pvti_bypass,
+ .short_help = "set interface ip6 pvti-bypass <interface> [del]",
+};
+
+static clib_error_t *
+pvti_interface_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = 0;
+
+ // pvti_main_t * pmp = &pvti_main;
+ u32 sw_if_index = ~0;
+ int rv = 0;
+ ip_address_t peer_ip = { 0 };
+ ip_address_t local_ip = { 0 };
+ u32 peer_port = 0;
+ u32 local_port = 12345;
+ u32 underlay_mtu = 1500;
+ u32 underlay_fib_index = ~0;
+ u32 underlay_table_id = ~0;
+ pvti_peer_address_method_t peer_address_method = PVTI_PEER_ADDRESS_FIXED;
+ bool peer_set = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "peer %U %d %d", unformat_ip_address, &peer_ip,
+ &peer_port, &local_port))
+ {
+ peer_set = 1;
+ }
+ else if (unformat (line_input, "underlay-mtu %d", &underlay_mtu))
+ {
+ // MTU set
+ }
+ else if (unformat (line_input, "local-ip %U", unformat_ip_address,
+ &local_ip))
+ {
+ // local IP set
+ }
+ else if (unformat (line_input, "underlay-fib %d", &underlay_fib_index))
+ {
+ // underlay fib set
+ }
+ else if (unformat (line_input, "peer-address-from-payload"))
+ {
+ peer_address_method = PVTI_PEER_ADDRESS_FROM_PAYLOAD;
+ }
+ else if (unformat (line_input, "underlay-table %d", &underlay_table_id))
+ {
+ fib_protocol_t fib_proto = FIB_PROTOCOL_IP4;
+ if (peer_ip.version == AF_IP6)
+ {
+ fib_proto = FIB_PROTOCOL_IP6;
+ }
+ u32 fib_index = fib_table_find (fib_proto, underlay_table_id);
+
+ if (~0 == fib_index)
+ {
+ error = clib_error_return (0, "Nonexistent table id %d",
+ underlay_table_id);
+ goto done;
+ }
+ underlay_fib_index = fib_index;
+ }
+ else
+ break;
+ }
+ if (!peer_set)
+ {
+ error = clib_error_return (0, "Please specify a peer...");
+ goto done;
+ }
+
+ rv = pvti_if_create (&local_ip, local_port, &peer_ip, peer_port,
+ peer_address_method, underlay_mtu, underlay_fib_index,
+ &sw_if_index);
+
+ switch (rv)
+ {
+ case 0:
+ break;
+
+ case VNET_API_ERROR_INVALID_SW_IF_INDEX:
+ error = clib_error_return (0, "Invalid interface");
+ break;
+
+ default:
+ error = clib_error_return (0, "pvti_if_create returned %d", rv);
+ }
+done:
+ unformat_free (line_input);
+ return error;
+}
+
+static clib_error_t *
+pvti_interface_delete_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ // pvti_main_t * pmp = &pvti_main;
+ u32 sw_if_index = ~0;
+ int rv = 0;
+ bool if_index_set = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "if-index %d", &sw_if_index))
+ {
+ if_index_set = 1;
+ }
+ else
+ break;
+ }
+ if (!if_index_set)
+ return clib_error_return (0, "Please specify a sw_if_index...");
+
+ rv = pvti_if_delete (sw_if_index);
+
+ switch (rv)
+ {
+ case 0:
+ break;
+
+ case VNET_API_ERROR_INVALID_SW_IF_INDEX:
+ return clib_error_return (0, "Invalid interface");
+ break;
+
+ default:
+ return clib_error_return (0, "pvti_if_delete returned %d", rv);
+ }
+ return 0;
+}
+
+VLIB_CLI_COMMAND (pvti_interface_create_command, static) = {
+ .path = "pvti interface create",
+ .short_help =
+ "pvti interface create peer <remote-ip> <remote-port> <local-port> [ "
+ "local-ip <ip-addr> ][ underlay-mtu <MTU>][underlay-table "
+ "<table-index>][inderlay-fib <fib-index>]",
+ .function = pvti_interface_create_command_fn,
+};
+
+VLIB_CLI_COMMAND (pvti_interface_delete_command, static) = {
+ .path = "pvti interface delete",
+ .short_help = "pvti interface delete if-index <sw-ifindex>",
+ .function = pvti_interface_delete_command_fn,
+};
+
+static clib_error_t *
+pvti_show_interface_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ pvti_if_t *pvti_if;
+ vec_foreach (pvti_if, pvti_main.if_pool)
+ {
+ int index = pvti_if - pvti_main.if_pool;
+ vlib_cli_output (vm, "%U", format_pvti_if, index);
+ };
+ return 0;
+}
+
+static clib_error_t *
+pvti_show_tx_peers_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ pvti_per_thread_data_t *ptd;
+ int is_ip6;
+ for (is_ip6 = 0; is_ip6 <= 1; is_ip6++)
+ {
+ vec_foreach (ptd, pvti_main.per_thread_data[is_ip6])
+ {
+ vlib_cli_output (vm, "thread %d (%s)",
+ ptd - pvti_main.per_thread_data[is_ip6],
+ is_ip6 ? "IPv6" : "IPv4");
+ pvti_tx_peer_t *peer;
+ vec_foreach (peer, ptd->tx_peers)
+ {
+ vlib_cli_output (vm, " %U", format_pvti_tx_peer_ptr, peer);
+ }
+ }
+ }
+ return 0;
+}
+
+static clib_error_t *
+pvti_show_rx_peers_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ pvti_per_thread_data_t *ptd;
+ int is_ip6;
+ for (is_ip6 = 0; is_ip6 <= 1; is_ip6++)
+ {
+ vec_foreach (ptd, pvti_main.per_thread_data[is_ip6])
+ {
+ vlib_cli_output (vm, "thread %d (%s)",
+ ptd - pvti_main.per_thread_data[is_ip6],
+ is_ip6 ? "IPv6" : "IPv4");
+ pvti_rx_peer_t *peer;
+ vec_foreach (peer, ptd->rx_peers)
+ {
+ vlib_cli_output (vm, " %U", format_pvti_rx_peer_ptr, peer);
+ }
+ }
+ }
+ return 0;
+}
+
+VLIB_CLI_COMMAND (pvti_show_interface_command, static) = {
+ .path = "show pvti interface",
+ .short_help = "show pvti interface",
+ .function = pvti_show_interface_command_fn,
+};
+
+VLIB_CLI_COMMAND (pvti_show_tx_peers_command, static) = {
+ .path = "show pvti tx peers",
+ .short_help = "show pvti tx peers",
+ .function = pvti_show_tx_peers_command_fn,
+};
+
+VLIB_CLI_COMMAND (pvti_show_rx_peers_command, static) = {
+ .path = "show pvti rx peers",
+ .short_help = "show pvti rx peers",
+ .function = pvti_show_rx_peers_command_fn,
+};
+
+void pvti_api_init ();
+
+VNET_FEATURE_INIT (pvti4_bypass, static) = {
+ .arc_name = "ip4-unicast",
+ .node_name = "ip4-pvti-bypass",
+ .runs_before = 0,
+};
+
+VNET_FEATURE_INIT (pvti6_bypass, static) = {
+ .arc_name = "ip6-unicast",
+ .node_name = "ip6-pvti-bypass",
+ .runs_before = 0,
+};
+
+static clib_error_t *
+pvti_early_config (vlib_main_t *vm, unformat_input_t *input)
+{
+ u8 *runs_before = 0;
+ int rbi = 0;
+ if (vec_len (vnet_feat_pvti4_bypass.runs_before) == 0)
+ {
+ rbi = 0;
+ }
+ else
+ {
+ rbi = vec_len (vnet_feat_pvti4_bypass.runs_before) - 1;
+ }
+ vec_validate (vnet_feat_pvti4_bypass.runs_before, rbi);
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "runs-before %v", &runs_before))
+ {
+ vec_add1 (runs_before, 0);
+ vnet_feat_pvti4_bypass.runs_before[rbi] = (char *) runs_before;
+ vec_add1 (vnet_feat_pvti4_bypass.runs_before, 0);
+ }
+ else
+ return clib_error_return (0, "unknown input");
+ }
+
+ return NULL;
+}
+
+VLIB_EARLY_CONFIG_FUNCTION (pvti_early_config, "pvti");
+
+static clib_error_t *
+pvti_init (vlib_main_t *vm)
+{
+ pvti_main_t *pmp = &pvti_main;
+ clib_error_t *error = 0;
+
+ pmp->vlib_main = vm;
+ pmp->vnet_main = vnet_get_main ();
+ pmp->is_initialized = 0;
+
+ pvti_api_init ();
+ return error;
+}
+
+VLIB_INIT_FUNCTION (pvti_init);
+
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Packet Vector Tunnel Interface plugin",
+};
diff --git a/src/plugins/pvti/pvti.h b/src/plugins/pvti/pvti.h
new file mode 100644
index 00000000000..ac097c5ecca
--- /dev/null
+++ b/src/plugins/pvti/pvti.h
@@ -0,0 +1,257 @@
+/*
+ * pvti.h - skeleton vpp engine plug-in header file
+ *
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_pvti_h__
+#define __included_pvti_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+
+#define VPP_MAX_THREADS (1 << 8)
+
+#define MAX_RX_STREAMS 256
+
+#define PVTI_ALIGN_BYTES 9
+
+typedef CLIB_PACKED (struct {
+ u32 seq;
+ u8 stream_index; // set to the cpu# on the sending side
+ u8 chunk_count;
+ u8 reass_chunk_count; // number of chunks in the front that are related to
+ // previously started buffer
+ // mandatory_flags_mask highlights which of the flags cause packet drop if
+ // not understood, and which of them can be just ignored.
+ u8 mandatory_flags_mask;
+ u8 flags_value;
+ u8 pad_bytes;
+ u8 pad[0];
+}) pvti_packet_header_t;
+
+typedef CLIB_PACKED (struct {
+ ip4_header_t ip4;
+ udp_header_t udp;
+ // not part of encap header pvti_packet_header_t pv;
+}) pvti_ip4_encap_header_t;
+
+typedef CLIB_PACKED (struct {
+ ip6_header_t ip6;
+ udp_header_t udp;
+ // not part of encap header pvti_packet_header_t pv;
+}) pvti_ip6_encap_header_t;
+
+typedef CLIB_PACKED (struct {
+ u16 total_chunk_length;
+ // More fragments: this chunk is not the last block fragment
+#define CHUNK_FLAGS_MF (1 << 0)
+ // More blocks: this block has chained blocks that follow
+#define CHUNK_FLAGS_MB (1 << 1)
+ u16 _pad0;
+ u32 _pad1;
+ u8 chunk_data[0];
+}) pvti_chunk_header_t;
+
+typedef struct
+{
+ // a buffer being built from the smaller packets
+ u32 bi0;
+
+ // how big can this buffer grow
+ u32 bi0_max_current_length;
+
+ // how many chunks are already in the buffer
+ u8 chunk_count;
+ // leading reassembly chunk count
+ u8 reass_chunk_count;
+
+ u32 current_tx_seq;
+} pvti_per_tx_stream_data_t;
+
+typedef struct
+{
+ /* The seq# that we last processed */
+ u32 last_rx_seq;
+
+ // a current buffer that is being reassembled
+ u32 rx_bi0;
+ // The root buffer, most of the times == rx_bi0 except in the case of chained
+ // buffers.
+ u32 rx_bi0_first;
+
+ // Next index for dispatch when the reassembly is done
+ u16 rx_next0;
+ // expected totall inner length for the packet
+ u16 rx_expected_inner_length;
+ u16 rx_received_inner_length;
+
+} pvti_per_rx_stream_data_t;
+
+typedef struct
+{
+ ip_address_t local_ip;
+ ip_address_t remote_ip;
+ u16 remote_port;
+ u16 local_port;
+ u16 underlay_mtu;
+ u32 underlay_fib_index;
+
+ u32 pvti_if_index;
+ bool deleted;
+ bool is_bo0_traced;
+
+ u32 bo0_max_current_length;
+
+ u8 chunk_count;
+ u8 reass_chunk_count;
+ u32 current_tx_seq;
+ vlib_buffer_t *bo0;
+
+} pvti_tx_peer_t;
+
+typedef struct
+{
+ ip_address_t local_ip;
+ ip_address_t remote_ip;
+ u16 remote_port;
+ u16 local_port;
+
+ pvti_per_rx_stream_data_t rx_streams[MAX_RX_STREAMS];
+
+ u32 pvti_if_index;
+ bool deleted;
+} pvti_rx_peer_t;
+
+typedef struct
+{
+ /* pool of destination-based structures which are used to build the packets
+ */
+ pvti_tx_peer_t *tx_peers;
+
+ /* vector of buffers to send */
+ u32 *pending_tx_buffers;
+ u16 *pending_tx_nexts;
+ /* pool of source-based structures for the remote peers' data tracking
+ */
+ pvti_rx_peer_t *rx_peers;
+
+ /* vector of buffers being decapsulated */
+ u32 *pending_rx_buffers;
+ u16 *pending_rx_nexts;
+
+} pvti_per_thread_data_t;
+
+typedef struct
+{
+ ip_address_t local_ip;
+ ip_address_t remote_ip;
+ u16 remote_port;
+ u16 local_port;
+ u16 underlay_mtu;
+ u32 underlay_fib_index;
+ bool peer_address_from_payload;
+ u64 created_at;
+
+ u32 sw_if_index;
+ u32 hw_if_index;
+
+ // per-stream data for TX
+ pvti_per_tx_stream_data_t tx_streams[256];
+ pvti_per_rx_stream_data_t rx_streams[256];
+
+} pvti_if_t;
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+
+ /* have we initialized the data structures ? */
+ bool is_initialized;
+
+ /* interface pool */
+ pvti_if_t *if_pool;
+
+ /* if_index in the pool above by sw_if_index */
+ index_t *if_index_by_sw_if_index;
+
+ /* indices by port */
+ index_t **if_indices_by_port;
+
+ /* per-thread data, ip4[0] and ip6[1] */
+ pvti_per_thread_data_t *per_thread_data[2];
+
+ /* on/off switch for the periodic function */
+ u8 periodic_timer_enabled;
+ /* Node index, non-zero if the periodic process has been created */
+ u32 periodic_node_index;
+
+ /* graph node state */
+ uword *bm_ip4_bypass_enabled_by_sw_if;
+ uword *bm_ip6_bypass_enabled_by_sw_if;
+
+ /* convenience */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+ ethernet_main_t *ethernet_main;
+} pvti_main_t;
+
+extern pvti_main_t pvti_main;
+
+extern vlib_node_registration_t pvti_node;
+extern vlib_node_registration_t pvti4_input_node;
+extern vlib_node_registration_t pvti4_output_node;
+extern vlib_node_registration_t pvti6_input_node;
+extern vlib_node_registration_t pvti6_output_node;
+extern vlib_node_registration_t pvti_periodic_node;
+
+always_inline u8
+pvti_get_stream_index (int is_ip6)
+{
+ u32 thread_index = vlib_get_thread_index ();
+
+ ASSERT ((thread_index & 0xffffff80) == 0);
+
+ u8 stream_index = (thread_index & 0x7f) | (is_ip6 ? 0x80 : 0);
+ return stream_index;
+}
+
+/* attempt to get a new buffer */
+always_inline u32
+pvti_get_new_buffer (vlib_main_t *vm)
+{
+ u32 bi0 = INDEX_INVALID;
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ {
+ return INDEX_INVALID;
+ }
+ vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
+ b0->current_data = 0;
+ b0->current_length = 0;
+ return bi0;
+}
+
+/* Periodic function events */
+#define PVTI_EVENT1 1
+#define PVTI_EVENT2 2
+#define PVTI_EVENT_PERIODIC_ENABLE_DISABLE 3
+
+void pvti_create_periodic_process (pvti_main_t *);
+void pvti_verify_initialized (pvti_main_t *pvm);
+
+#endif /* __included_pvti_h__ */
diff --git a/src/plugins/pvti/pvti_if.c b/src/plugins/pvti/pvti_if.c
new file mode 100644
index 00000000000..4f83994a1a4
--- /dev/null
+++ b/src/plugins/pvti/pvti_if.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/adj/adj_midchain.h>
+#include <vnet/udp/udp.h>
+
+#include <pvti/pvti.h>
+#include <pvti/pvti_if.h>
+
+static u8 *
+format_pvti_if_name (u8 *s, va_list *args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ // wg_if_t *wgi = wg_if_get (dev_instance);
+ return format (s, "pvti%d", dev_instance);
+}
+
+u8 *
+format_pvti_if (u8 *s, va_list *args)
+{
+ index_t pvtii = va_arg (*args, u32);
+ pvti_if_t *pvti_if = pvti_if_get (pvtii);
+
+ s = format (
+ s, "[%d] %U local:%U:%d remote:%U:%d underlay_mtu:%d underlay_fib_idx:%d",
+ pvtii, format_vnet_sw_if_index_name, vnet_get_main (),
+ pvti_if->sw_if_index, format_ip46_address, &pvti_if->local_ip,
+ IP46_TYPE_ANY, pvti_if->local_port, format_ip46_address,
+ &pvti_if->remote_ip, IP46_TYPE_ANY, pvti_if->remote_port,
+ pvti_if->underlay_mtu, pvti_if->underlay_fib_index);
+
+ return (s);
+}
+
+index_t
+pvti_if_find_by_sw_if_index (u32 sw_if_index)
+{
+ if (vec_len (pvti_main.if_index_by_sw_if_index) <= sw_if_index)
+ return INDEX_INVALID;
+ u32 ti = pvti_main.if_index_by_sw_if_index[sw_if_index];
+ if (ti == ~0)
+ return INDEX_INVALID;
+
+ return (ti);
+}
+
+index_t
+pvti_if_find_by_remote_ip4_and_port (ip4_address_t *remote_ip4,
+ u16 remote_port)
+{
+ pvti_if_t *ifc;
+ pool_foreach (ifc, pvti_main.if_pool)
+ {
+ if ((ifc->remote_port == remote_port) &&
+ (ifc->remote_ip.version == AF_IP4) &&
+ ((ifc->remote_ip.ip.ip4.as_u32 == remote_ip4->as_u32) ||
+ ifc->peer_address_from_payload))
+ {
+ return (ifc - pvti_main.if_pool);
+ }
+ }
+ return INDEX_INVALID;
+}
+
+index_t
+pvti_if_find_by_remote_ip6_and_port (ip6_address_t *remote_ip6,
+ u16 remote_port)
+{
+ pvti_if_t *ifc;
+ pool_foreach (ifc, pvti_main.if_pool)
+ {
+ if ((ifc->remote_port == remote_port) &&
+ (ifc->remote_ip.version == AF_IP6) &&
+ ((0 == memcmp (&ifc->remote_ip.ip.ip6, remote_ip6,
+ sizeof (*remote_ip6))) ||
+ ifc->peer_address_from_payload))
+ {
+ return (ifc - pvti_main.if_pool);
+ }
+ }
+ return INDEX_INVALID;
+}
+
+index_t
+pvti_if_find_by_remote_ip_and_port (ip_address_t *remote_ip, u16 remote_port)
+{
+ pvti_if_t *ifc;
+ pool_foreach (ifc, pvti_main.if_pool)
+ {
+ if ((ifc->remote_port == remote_port) &&
+ (ifc->peer_address_from_payload ||
+ (0 == ip_address_cmp (remote_ip, &ifc->remote_ip))))
+ {
+ return (ifc - pvti_main.if_pool);
+ }
+ }
+ return INDEX_INVALID;
+}
+
+static void
+pvti_add_tidx_by_port (index_t t_index, u16 port)
+{
+ pvti_main_t *pvm = &pvti_main;
+ vec_validate_init_empty (pvm->if_indices_by_port, port, NULL);
+ vec_add1 (pvm->if_indices_by_port[port], t_index);
+}
+
+static void
+pvti_del_tidx_by_port (index_t t_index, u16 port)
+{
+ pvti_main_t *pvm = &pvti_main;
+ index_t *ii;
+ if (!pvm->if_indices_by_port)
+ {
+ return;
+ }
+ if (port >= vec_len (pvm->if_indices_by_port))
+ {
+ return;
+ }
+ if (vec_len (pvm->if_indices_by_port[port]) == 0)
+ {
+ ALWAYS_ASSERT (pvm->if_indices_by_port[port] > 0);
+ /* not reached */
+ return;
+ }
+
+ vec_foreach (ii, pvm->if_indices_by_port[port])
+ {
+ if (*ii == t_index)
+ {
+ vec_del1 (pvm->if_indices_by_port[port],
+ pvm->if_indices_by_port[port] - ii);
+ break;
+ }
+ }
+}
+
+static u32
+pvti_get_tunnel_count_by_port (u16 port)
+{
+ pvti_main_t *pvm = &pvti_main;
+ if (!pvm->if_indices_by_port)
+ {
+ return 0;
+ }
+ return vec_len (vec_elt (pvm->if_indices_by_port, port));
+}
+
+static clib_error_t *
+pvti_if_admin_up_down (vnet_main_t *vnm, u32 hw_if_index, u32 flags)
+{
+ // vnet_hw_interface_t *hi;
+ u32 hw_flags;
+
+ // hi = vnet_get_hw_interface (vnm, hw_if_index);
+ hw_flags =
+ (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? VNET_HW_INTERFACE_FLAG_LINK_UP :
+ 0);
+ vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
+
+ return (NULL);
+}
+
+void
+pvti_if_update_adj (vnet_main_t *vnm, u32 sw_if_index, adj_index_t ai)
+{
+
+ /* Convert any neighbour adjacency that has a next-hop reachable through
+ * the wg interface into a midchain. This is to avoid sending ARP/ND to
+ * resolve the next-hop address via the wg interface. Then, if one of the
+ * peers has matching prefix among allowed prefixes, the midchain will be
+ * updated to the corresponding one.
+ */
+ adj_nbr_midchain_update_rewrite (ai, NULL, NULL, ADJ_FLAG_NONE, NULL);
+
+ // wgii = wg_if_find_by_sw_if_index (sw_if_index);
+ // wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_adj_change, &ai);
+}
+
+VNET_DEVICE_CLASS (pvti_if_device_class) = {
+ .name = "Packet Vectorizer Tunnel",
+ .format_device_name = format_pvti_if_name,
+ .admin_up_down_function = pvti_if_admin_up_down,
+};
+
+VNET_HW_INTERFACE_CLASS (pvti_hw_interface_class) = {
+ .name = "PVTunnel",
+ .update_adjacency = pvti_if_update_adj,
+ .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+ // .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
+};
+
+int
+pvti_if_create (ip_address_t *local_ip, u16 local_port,
+ ip_address_t *remote_ip, u16 remote_port,
+ pvti_peer_address_method_t peer_address_method,
+ u16 underlay_mtu, u32 underlay_fib_index, u32 *sw_if_indexp)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ pvti_main_t *pvm = &pvti_main;
+ u32 hw_if_index;
+ vnet_hw_interface_t *hi;
+ pvti_verify_initialized (pvm);
+
+ pvti_if_t *pvti_if;
+
+ ASSERT (sw_if_indexp);
+
+ *sw_if_indexp = (u32) ~0;
+
+ pool_get_zero (pvti_main.if_pool, pvti_if);
+ pvti_if->local_ip = *local_ip;
+ pvti_if->local_port = local_port;
+ pvti_if->remote_ip = *remote_ip;
+ if (peer_address_method == PVTI_PEER_ADDRESS_FROM_PAYLOAD)
+ {
+ pvti_if->peer_address_from_payload = 1;
+ }
+ pvti_if->remote_port = remote_port;
+ pvti_if->underlay_mtu = underlay_mtu;
+ pvti_if->underlay_fib_index = underlay_fib_index;
+ pvti_if->created_at = clib_cpu_time_now ();
+
+ /* tunnel index (or instance) */
+ u32 t_idx = pvti_if - pvti_main.if_pool;
+
+ hw_if_index =
+ vnet_register_interface (vnm, pvti_if_device_class.index, t_idx,
+ pvti_hw_interface_class.index, t_idx);
+
+ pvti_if->hw_if_index = hw_if_index;
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+ pvti_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
+
+ vec_validate_init_empty (pvm->if_index_by_sw_if_index, hi->sw_if_index,
+ INDEX_INVALID);
+
+ vec_elt (pvm->if_index_by_sw_if_index, hi->sw_if_index) = t_idx;
+ pvti_if_t *pvti_if0 = pool_elt_at_index (pvti_main.if_pool, t_idx);
+ int i;
+ for (i = 0; i < 256; i++)
+ {
+ pvti_if0->tx_streams[i].bi0 = INDEX_INVALID;
+ pvti_if0->tx_streams[i].current_tx_seq = 42;
+
+ pvti_if0->rx_streams[i].rx_bi0 = INDEX_INVALID;
+ pvti_if0->rx_streams[i].rx_bi0_first = INDEX_INVALID;
+ }
+
+ /*
+ int is_ip6 = 0;
+ u32 encap_index = !is_ip6 ?
+ pvti4_output_node.index : pvti6_output_node.index;
+ vnet_set_interface_output_node (vnm, pvti_if->hw_if_index, encap_index);
+ */
+ vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index,
+ (u8 *) "pvti4-output");
+
+ pvti_add_tidx_by_port (t_idx, local_port);
+ if (1 == pvti_get_tunnel_count_by_port (local_port))
+ {
+ clib_warning ("Registering local port %d", local_port);
+ udp_register_dst_port (vlib_get_main (), local_port,
+ pvti4_input_node.index, UDP_IP4);
+ udp_register_dst_port (vlib_get_main (), local_port,
+ pvti6_input_node.index, UDP_IP6);
+ }
+ else
+ {
+ clib_warning ("Not registering the port");
+ }
+
+ vnet_hw_interface_set_flags (vnm, pvti_if->hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+
+ return 0;
+}
+
+void
+pvti_if_walk (pvti_if_walk_cb_t fn, void *data)
+{
+ index_t pvtii;
+
+ pool_foreach_index (pvtii, pvti_main.if_pool)
+ {
+ if (WALK_STOP == fn (pvtii, data))
+ break;
+ }
+}
+
+int
+pvti_if_delete (u32 sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ pvti_main_t *pvm = &pvti_main;
+
+ if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+ if (hw == 0 || hw->dev_class_index != pvti_if_device_class.index)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ pvti_if_t *ifc;
+ bool found = 0;
+ pool_foreach (ifc, pvm->if_pool)
+ {
+ if (ifc->sw_if_index == sw_if_index)
+ {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ return VNET_API_ERROR_INVALID_VALUE_2;
+ }
+ index_t tidx = ifc - pvm->if_pool;
+
+ u16 local_port = ifc->local_port;
+ pvti_del_tidx_by_port (tidx, local_port);
+ pvm->if_index_by_sw_if_index[sw_if_index] = INDEX_INVALID;
+
+ if (0 == pvti_get_tunnel_count_by_port (local_port))
+ {
+ udp_unregister_dst_port (vlib_get_main (), local_port, 1);
+ udp_unregister_dst_port (vlib_get_main (), local_port, 0);
+ }
+
+ vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
+ vnet_delete_hw_interface (vnm, hw->hw_if_index);
+ pool_put (pvti_main.if_pool, ifc);
+
+ /* mark per-thread peers as deleted */
+ pvti_per_thread_data_t *ptd;
+
+ vec_foreach (ptd, pvm->per_thread_data[0])
+ {
+ pvti_tx_peer_t *peer;
+ vec_foreach (peer, ptd->tx_peers)
+ {
+ if (tidx == peer->pvti_if_index)
+ {
+ peer->deleted = 1;
+ }
+ }
+ }
+ vec_foreach (ptd, pvm->per_thread_data[1])
+ {
+ pvti_tx_peer_t *peer;
+ vec_foreach (peer, ptd->tx_peers)
+ {
+ if (tidx == peer->pvti_if_index)
+ {
+ peer->deleted = 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/plugins/pvti/pvti_if.h b/src/plugins/pvti/pvti_if.h
new file mode 100644
index 00000000000..44bf22ce825
--- /dev/null
+++ b/src/plugins/pvti/pvti_if.h
@@ -0,0 +1,47 @@
+#ifndef PVTI_IF_H
+#define PVTI_IF_H
+
+#include <vnet/interface_funcs.h>
+
+typedef enum
+{
+ PVTI_PEER_ADDRESS_FIXED = 0,
+ PVTI_PEER_ADDRESS_FROM_PAYLOAD
+} pvti_peer_address_method_t;
+
+typedef walk_rc_t (*pvti_if_walk_cb_t) (index_t wgi, void *data);
+void pvti_if_walk (pvti_if_walk_cb_t fn, void *data);
+
+int pvti_if_create (ip_address_t *local_ip, u16 local_port,
+ ip_address_t *remote_ip, u16 remote_port,
+ pvti_peer_address_method_t peer_address_method,
+ u16 underlay_mtu, u32 underlay_fib_index,
+ u32 *sw_if_indexp);
+index_t pvti_if_find_by_sw_if_index (u32 sw_if_index);
+index_t pvti_if_find_by_remote_ip4_and_port (ip4_address_t *remote_ip4,
+ u16 remote_port);
+index_t pvti_if_find_by_remote_ip6_and_port (ip6_address_t *remote_ip4,
+ u16 remote_port);
+
+index_t pvti_if_find_by_remote_ip_and_port (ip_address_t *remote_ip,
+ u16 remote_port);
+
+int pvti_if_delete (u32 sw_if_index);
+
+u8 *format_pvti_if (u8 *s, va_list *args);
+
+static_always_inline pvti_if_t *
+pvti_if_get (index_t pvtii)
+{
+ if (INDEX_INVALID == pvtii)
+ return (NULL);
+ return (pool_elt_at_index (pvti_main.if_pool, pvtii));
+}
+
+static_always_inline index_t
+pvti_if_get_index (pvti_if_t *pvti_if)
+{
+ return pvti_if - pvti_main.if_pool;
+}
+
+#endif
diff --git a/src/plugins/quic/quic.c b/src/plugins/quic/quic.c
index 60d4ac21c19..3f7a3426069 100644
--- a/src/plugins/quic/quic.c
+++ b/src/plugins/quic/quic.c
@@ -1058,6 +1058,8 @@ quic_on_stream_open (quicly_stream_open_t * self, quicly_stream_t * stream)
svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
+ svm_fifo_init_ooo_lookup (stream_session->rx_fifo, 0 /* ooo enq */);
+ svm_fifo_init_ooo_lookup (stream_session->tx_fifo, 1 /* ooo deq */);
stream_session->session_state = SESSION_STATE_ACCEPTING;
if ((rv = app_worker_accept_notify (app_wrk, stream_session)))
@@ -1302,6 +1304,8 @@ quic_connect_stream (session_t * quic_session, session_endpoint_cfg_t * sep)
return app_worker_connect_notify (app_wrk, NULL, rv, sep->opaque);
}
+ svm_fifo_init_ooo_lookup (stream_session->rx_fifo, 0 /* ooo enq */);
+ svm_fifo_init_ooo_lookup (stream_session->tx_fifo, 1 /* ooo deq */);
svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
@@ -1679,6 +1683,9 @@ quic_on_quic_session_connected (quic_ctx_t * ctx)
return;
}
+ svm_fifo_init_ooo_lookup (quic_session->rx_fifo, 0 /* ooo enq */);
+ svm_fifo_init_ooo_lookup (quic_session->tx_fifo, 1 /* ooo deq */);
+
quic_session->session_state = SESSION_STATE_CONNECTING;
if ((rv = app_worker_connect_notify (app_wrk, quic_session,
SESSION_E_NONE, ctx->client_opaque)))
@@ -2137,6 +2144,9 @@ quic_accept_connection (quic_rx_packet_ctx_t * pctx)
return;
}
+ svm_fifo_init_ooo_lookup (quic_session->rx_fifo, 0 /* ooo enq */);
+ svm_fifo_init_ooo_lookup (quic_session->tx_fifo, 1 /* ooo deq */);
+
app_wrk = app_worker_get (quic_session->app_wrk_index);
quic_session->session_state = SESSION_STATE_ACCEPTING;
if ((rv = app_worker_accept_notify (app_wrk, quic_session)))
diff --git a/src/plugins/srmpls/CMakeLists.txt b/src/plugins/srmpls/CMakeLists.txt
new file mode 100644
index 00000000000..25905d31e1b
--- /dev/null
+++ b/src/plugins/srmpls/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (c) 2024 Cisco and/or its affiliates
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_vpp_plugin(srmpls
+ SOURCES
+ sr_mpls_policy.c
+ sr_mpls_steering.c
+ sr_mpls_api.c
+ plugin.c
+
+ INSTALL_HEADERS
+ sr_mpls.h
+
+ API_FILES
+ sr_mpls.api
+
+ # This might need to be VAT_AUTO_TEST? Not documented
+ API_TEST_SOURCES
+ sr_mpls_test.c
+)
diff --git a/src/vnet/srmpls/FEATURE.yaml b/src/plugins/srmpls/FEATURE.yaml
index c5b958224c7..c5b958224c7 100644
--- a/src/vnet/srmpls/FEATURE.yaml
+++ b/src/plugins/srmpls/FEATURE.yaml
diff --git a/src/vnet/srmpls/dir.dox b/src/plugins/srmpls/dir.dox
index 76ec1d6a41b..76ec1d6a41b 100644
--- a/src/vnet/srmpls/dir.dox
+++ b/src/plugins/srmpls/dir.dox
diff --git a/src/plugins/marvell/plugin.c b/src/plugins/srmpls/plugin.c
index ed90776ba95..af87607764f 100644
--- a/src/plugins/marvell/plugin.c
+++ b/src/plugins/srmpls/plugin.c
@@ -1,6 +1,7 @@
/*
- *------------------------------------------------------------------
- * Copyright (c) 2018 Cisco and/or its affiliates.
+ * plugin.c: srmpls
+ *
+ * Copyright (c) 2024 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -12,22 +13,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *------------------------------------------------------------------
*/
#include <vlib/vlib.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
+// register a plugin
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
- .description = "Marvell PP2 Device Driver",
+ .description = "Segment Routing for MPLS plugin",
};
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vnet/srmpls/sr_doc.rst b/src/plugins/srmpls/sr_doc.rst
index ed847fa0d42..ed847fa0d42 100644
--- a/src/vnet/srmpls/sr_doc.rst
+++ b/src/plugins/srmpls/sr_doc.rst
diff --git a/src/vnet/srmpls/sr_mpls.api b/src/plugins/srmpls/sr_mpls.api
index 742f135d493..742f135d493 100644
--- a/src/vnet/srmpls/sr_mpls.api
+++ b/src/plugins/srmpls/sr_mpls.api
diff --git a/src/vnet/srmpls/sr_mpls.h b/src/plugins/srmpls/sr_mpls.h
index a8f9494428f..a8f9494428f 100644
--- a/src/vnet/srmpls/sr_mpls.h
+++ b/src/plugins/srmpls/sr_mpls.h
diff --git a/src/vnet/srmpls/sr_mpls_api.c b/src/plugins/srmpls/sr_mpls_api.c
index 920856acff6..3e89017dbc1 100644
--- a/src/vnet/srmpls/sr_mpls_api.c
+++ b/src/plugins/srmpls/sr_mpls_api.c
@@ -17,7 +17,7 @@
*/
#include <vnet/vnet.h>
-#include <vnet/srmpls/sr_mpls.h>
+#include "sr_mpls.h"
#include <vlibmemory/api.h>
#include <vnet/interface.h>
@@ -26,28 +26,27 @@
#include <vnet/ip/ip_types_api.h>
#include <vnet/format_fns.h>
-#include <vnet/srmpls/sr_mpls.api_enum.h>
-#include <vnet/srmpls/sr_mpls.api_types.h>
-
+#include <plugins/srmpls/sr_mpls.api_enum.h>
+#include <plugins/srmpls/sr_mpls.api_types.h>
#define vl_api_version(n, v) static u32 api_version = v;
-#include <vnet/srmpls/sr_mpls.api.h>
+#include <plugins/srmpls/sr_mpls.api.h>
#undef vl_api_version
#define vl_endianfun
-#include <vnet/srmpls/sr_mpls.api.h>
+#include <plugins/srmpls/sr_mpls.api.h>
#undef vl_endianfun
#define vl_calcsizefun
-#include <vnet/srmpls/sr_mpls.api.h>
+#include <plugins/srmpls/sr_mpls.api.h>
#undef vl_calcsizefun
#define vl_printfun
-#include <vnet/srmpls/sr_mpls.api.h>
+#include <plugins/srmpls/sr_mpls.api.h>
#undef vl_printfun
#define vl_msg_name_crc_list
-#include <vnet/srmpls/sr_mpls.api.h>
+#include <plugins/srmpls/sr_mpls.api.h>
#undef vl_msg_name_crc_list
#define REPLY_MSG_ID_BASE msg_id_base
diff --git a/src/vnet/srmpls/sr_mpls_policy.c b/src/plugins/srmpls/sr_mpls_policy.c
index 41cb71601e9..af24acd8cf6 100644
--- a/src/vnet/srmpls/sr_mpls_policy.c
+++ b/src/plugins/srmpls/sr_mpls_policy.c
@@ -31,7 +31,7 @@
#include <vlib/vlib.h>
#include <vnet/vnet.h>
-#include <vnet/srmpls/sr_mpls.h>
+#include "sr_mpls.h"
#include <vnet/fib/mpls_fib.h>
#include <vnet/dpo/dpo.h>
#include <vnet/ip/ip.h>
diff --git a/src/vnet/srmpls/sr_mpls_steering.c b/src/plugins/srmpls/sr_mpls_steering.c
index e8920df542b..24c8b0e2d9f 100644
--- a/src/vnet/srmpls/sr_mpls_steering.c
+++ b/src/plugins/srmpls/sr_mpls_steering.c
@@ -31,7 +31,7 @@
#include <vlib/vlib.h>
#include <vnet/vnet.h>
-#include <vnet/srmpls/sr_mpls.h>
+#include "sr_mpls.h"
#include <vnet/ip/ip4_packet.h>
#include <vnet/ip/ip6_packet.h>
#include <vnet/fib/mpls_fib.h>
diff --git a/src/vnet/srmpls/sr_mpls_test.c b/src/plugins/srmpls/sr_mpls_test.c
index e5d68462443..7aff4c32b06 100644
--- a/src/vnet/srmpls/sr_mpls_test.c
+++ b/src/plugins/srmpls/sr_mpls_test.c
@@ -25,11 +25,11 @@
/* Declare message IDs */
#include <vnet/format_fns.h>
-#include <vnet/srmpls/sr_mpls.api_enum.h>
-#include <vnet/srmpls/sr_mpls.api_types.h>
+#include <plugins/srmpls/sr_mpls.api_enum.h>
+#include <plugins/srmpls/sr_mpls.api_types.h>
#define vl_endianfun /* define message structures */
-#include <vnet/srmpls/sr_mpls.api.h>
+#include <plugins/srmpls/sr_mpls.api.h>
#undef vl_endianfun
typedef struct
@@ -163,7 +163,7 @@ api_sr_mpls_policy_del (vat_main_t *vam)
return ret;
}
-#include <vnet/srmpls/sr_mpls.api_test.c>
+#include <plugins/srmpls/sr_mpls.api_test.c>
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/tlsopenssl/tls_openssl.c b/src/plugins/tlsopenssl/tls_openssl.c
index 5d172a0adcf..19aae3ffadc 100644
--- a/src/plugins/tlsopenssl/tls_openssl.c
+++ b/src/plugins/tlsopenssl/tls_openssl.c
@@ -1286,7 +1286,10 @@ tls_openssl_set_command_fn (vlib_main_t * vm, unformat_input_t * input,
}
else
{
- vnet_session_enable_disable (vm, 1);
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
if (openssl_engine_register (engine_name, engine_alg, async) < 0)
{
return clib_error_return (0, "Failed to register %s polling",
diff --git a/src/plugins/unittest/fib_test.c b/src/plugins/unittest/fib_test.c
index fbac809d726..491d135322c 100644
--- a/src/plugins/unittest/fib_test.c
+++ b/src/plugins/unittest/fib_test.c
@@ -10264,7 +10264,57 @@ fib_test_inherit (void)
&l99_o_10_10_10_3),
"%U via interposer label",
format_fib_prefix,&pfx_10_10_10_21_s_32);
+ fib_table_entry_special_remove(0,
+ &pfx_10_10_10_0_s_24,
+ FIB_SOURCE_SPECIAL);
+
+ const ip46_address_t nh_0_0_0_0 = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x00000000),
+ };
+ const fib_prefix_t pfx_0_0_0_0_s_0 = {
+ .fp_len = 0,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = nh_0_0_0_0,
+ };
+ /* we have prio(API) < prio(hi_src) < prio(SPECIAL) */
+ /* Add/remove an interposer source from the top of the subtrie. The
+ * interposer source is inherited.
+ */
+ fib_table_entry_special_dpo_add(0,
+ &pfx_0_0_0_0_s_0,
+ hi_src,
+ (FIB_ENTRY_FLAG_COVERED_INHERIT |
+ FIB_ENTRY_FLAG_INTERPOSE),
+ &interposer);
+ /*
+ * Add/remove an interposer source from the top of the subtrie. The
+ * interposer source is inherited, the previous inheritance is discarded.
+ */
+ fib_table_entry_special_dpo_add(0,
+ &pfx_10_10_10_0_s_24,
+ FIB_SOURCE_SPECIAL,
+ (FIB_ENTRY_FLAG_COVERED_INHERIT |
+ FIB_ENTRY_FLAG_INTERPOSE),
+ &interposer);
+ /* force a tree walk */
+ fib_table_entry_update_one_path(0,
+ &pfx_0_0_0_0_s_0,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ DPO_PROTO_IP4,
+ &nh_10_10_10_3,
+ tm->hw[0]->sw_if_index,
+ ~0,
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ fib_table_entry_special_remove(0,
+ &pfx_10_10_10_0_s_24,
+ FIB_SOURCE_SPECIAL);
+ fib_table_entry_special_remove(0,
+ &pfx_0_0_0_0_s_0,
+ hi_src);
/*
* cleanup
*/
@@ -10275,6 +10325,7 @@ fib_test_inherit (void)
fib_table_entry_delete(0, &pfx_10_10_10_0_s_24, FIB_SOURCE_API);
fib_table_entry_delete(0, &pfx_10_10_0_0_s_16, FIB_SOURCE_API);
fib_table_entry_delete(0, &pfx_10_10_10_0_s_24, FIB_SOURCE_SPECIAL);
+ fib_table_entry_delete(0, &pfx_0_0_0_0_s_0, FIB_SOURCE_API);
adj_unlock(ai_10_10_10_1);
adj_unlock(ai_10_10_10_2);
adj_unlock(ai_10_10_10_3);
diff --git a/src/plugins/unittest/segment_manager_test.c b/src/plugins/unittest/segment_manager_test.c
index a106470ee48..29da662e007 100644
--- a/src/plugins/unittest/segment_manager_test.c
+++ b/src/plugins/unittest/segment_manager_test.c
@@ -739,8 +739,11 @@ segment_manager_test (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd_arg)
{
int res = 0;
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
- vnet_session_enable_disable (vm, 1);
+ vnet_session_enable_disable (vm, &args);
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
diff --git a/src/plugins/unittest/session_test.c b/src/plugins/unittest/session_test.c
index b7627acc129..a70a6ea88bf 100644
--- a/src/plugins/unittest/session_test.c
+++ b/src/plugins/unittest/session_test.c
@@ -13,13 +13,11 @@
* limitations under the License.
*/
-#include <vnet/session/application_namespace.h>
-#include <vnet/session/application_interface.h>
+#include <arpa/inet.h>
#include <vnet/session/application.h>
#include <vnet/session/session.h>
-#include <vnet/session/session_rules_table.h>
-#include <vnet/tcp/tcp.h>
#include <sys/epoll.h>
+#include <vnet/session/session_rules_table.h>
#define SESSION_TEST_I(_cond, _comment, _args...) \
({ \
@@ -133,7 +131,8 @@ session_create_lookpback (u32 table_id, u32 * sw_if_index,
if (table_id != 0)
{
- ip_table_create (FIB_PROTOCOL_IP4, table_id, 0, 0);
+ ip_table_create (FIB_PROTOCOL_IP4, table_id, 0 /* is_api */,
+ 1 /* create_mfib */, 0);
ip_table_bind (FIB_PROTOCOL_IP4, *sw_if_index, table_id);
}
@@ -774,10 +773,37 @@ session_test_namespace (vlib_main_t * vm, unformat_input_t * input)
return 0;
}
+static void
+session_test_disable_rt_backend_engine (vlib_main_t *vm)
+{
+ session_enable_disable_args_t args = { .is_en = 0,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_DISABLE };
+ vnet_session_enable_disable (vm, &args);
+}
+
+static void
+session_test_enable_rule_table_engine (vlib_main_t *vm)
+{
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
+}
+
+static void
+session_test_enable_sdl_engine (vlib_main_t *vm)
+{
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_SDL };
+ vnet_session_enable_disable (vm, &args);
+}
+
static int
session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
{
- session_rules_table_t _srt, *srt = &_srt;
+ session_table_t *st = session_table_alloc ();
u16 lcl_port = 1234, rmt_port = 4321;
u32 action_index = 1, res;
ip4_address_t lcl_lkup, rmt_lkup;
@@ -795,8 +821,11 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
}
}
- clib_memset (srt, 0, sizeof (*srt));
- session_rules_table_init (srt);
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_rule_table_engine (vm);
+
+ session_table_init (st, FIB_PROTOCOL_MAX);
+ session_rules_table_init (st, FIB_PROTOCOL_MAX);
ip4_address_t lcl_ip = {
.as_u32 = clib_host_to_net_u32 (0x01020304),
@@ -835,12 +864,13 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
.action_index = action_index++,
.is_add = 1,
};
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action %d",
action_index - 1);
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 1),
"Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 1: %d",
res);
@@ -851,13 +881,15 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.lcl.fp_addr.ip4 = lcl_ip;
args.lcl.fp_len = 24;
args.action_index = action_index++;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/16 4321 action %d",
action_index - 1);
args.rmt.fp_addr.ip4 = rmt_ip;
args.rmt.fp_len = 24;
args.action_index = action_index++;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/24 4321 action %d",
action_index - 1);
@@ -869,13 +901,15 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.rmt.fp_addr.ip4 = rmt_ip2;
args.rmt.fp_len = 16;
args.action_index = action_index++;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 2.2.2.2/24 1234 6.6.6.6/16 4321 action %d",
action_index - 1);
args.lcl.fp_addr.ip4 = lcl_ip3;
args.rmt.fp_addr.ip4 = rmt_ip3;
args.action_index = action_index++;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 3.3.3.3/24 1234 7.7.7.7/16 4321 action %d",
action_index - 1);
@@ -885,7 +919,8 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.lcl.fp_addr.ip4 = lcl_ip3;
args.rmt.fp_addr.ip4 = rmt_ip3;
args.action_index = action_index++;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "overwrite 3.3.3.3/24 1234 7.7.7.7/16 4321 "
"action %d", action_index - 1);
@@ -893,23 +928,22 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
* Lookup 1.2.3.4/32 1234 5.6.7.8/32 4321, 1.2.2.4/32 1234 5.6.7.9/32 4321
* and 3.3.3.3 1234 7.7.7.7 4321
*/
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 3),
"Lookup 1.2.3.4 1234 5.6.7.8 4321 action " "should be 3: %d",
res);
lcl_lkup.as_u32 = clib_host_to_net_u32 (0x01020204);
rmt_lkup.as_u32 = clib_host_to_net_u32 (0x05060709);
- res =
- session_rules_table_lookup4 (srt, &lcl_lkup,
- &rmt_lkup, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_lkup, &rmt_lkup, lcl_port, rmt_port);
SESSION_TEST ((res == 1),
"Lookup 1.2.2.4 1234 5.6.7.9 4321, action " "should be 1: %d",
res);
- res =
- session_rules_table_lookup4 (srt, &lcl_ip3, &rmt_ip3, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip3, &rmt_ip3, lcl_port, rmt_port);
SESSION_TEST ((res == 6),
"Lookup 3.3.3.3 1234 7.7.7.7 4321, action "
"should be 6 (updated): %d", res);
@@ -925,17 +959,17 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.lcl_port = 0;
args.rmt_port = 0;
args.action_index = action_index++;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 1.2.3.4/24 * 5.6.7.8/24 * action %d",
action_index - 1);
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 7),
"Lookup 1.2.3.4 1234 5.6.7.8 4321, action should"
" be 7 (lpm dst): %d", res);
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip,
- lcl_port + 1, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port + 1, rmt_port);
SESSION_TEST ((res == 7),
"Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 7: %d",
res);
@@ -947,7 +981,8 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
* 1.2.3.4 1235 5.6.7.8 4322
*/
args.is_add = 0;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Del 1.2.3.4/24 * 5.6.7.8/24 *");
args.lcl.fp_addr.ip4 = lcl_ip;
@@ -958,7 +993,8 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.rmt_port = 0;
args.action_index = action_index++;
args.is_add = 1;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 1.2.3.4/16 * 5.6.7.8/16 * action %d",
action_index - 1);
@@ -970,27 +1006,28 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.rmt_port = rmt_port;
args.action_index = action_index++;
args.is_add = 1;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1235 5.6.7.8/24 4321 action %d",
action_index - 1);
if (verbose)
- session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4);
+ session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP,
+ FIB_PROTOCOL_IP4);
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 3),
"Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d",
res);
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip,
- lcl_port + 1, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port + 1, rmt_port);
SESSION_TEST ((res == 9),
"Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 9: %d",
res);
res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip,
- lcl_port + 1, rmt_port + 1);
+ session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, &lcl_ip,
+ &rmt_ip, lcl_port + 1, rmt_port + 1);
SESSION_TEST ((res == 8),
"Lookup 1.2.3.4 1235 5.6.7.8 4322, action should " "be 8: %d",
res);
@@ -1004,10 +1041,11 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.lcl.fp_len = 16;
args.rmt.fp_len = 16;
args.is_add = 0;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Del 1.2.0.0/16 1234 5.6.0.0/16 4321");
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 3),
"Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d",
res);
@@ -1015,10 +1053,11 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.lcl_port = 0;
args.rmt_port = 0;
args.is_add = 0;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Del 1.2.0.0/16 * 5.6.0.0/16 *");
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 3),
"Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d",
res);
@@ -1033,12 +1072,15 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
args.lcl_port = 1234;
args.rmt_port = 4321;
args.is_add = 0;
- error = session_rules_table_add_del (srt, &args);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
SESSION_TEST ((error == 0), "Del 1.2.3.4/24 1234 5.6.7.5/24");
- res =
- session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
SESSION_TEST ((res == 2), "Action should be 2: %d", res);
+ session_table_free (st, FIB_PROTOCOL_MAX);
+
return 0;
}
@@ -1074,6 +1116,9 @@ session_test_rules (vlib_main_t * vm, unformat_input_t * input)
}
}
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_rule_table_engine (vm);
+
server_sep.is_ip4 = 1;
server_sep.port = placeholder_port;
clib_memset (options, 0, sizeof (options));
@@ -2073,13 +2118,284 @@ session_test_mq_basic (vlib_main_t * vm, unformat_input_t * input)
return 0;
}
+static f32
+session_get_memory_usage (void)
+{
+ clib_mem_heap_t *heap = clib_mem_get_per_cpu_heap ();
+ u8 *s = 0;
+ char *ss;
+ f32 used = 0.0;
+
+ s = format (s, "%U\n", format_clib_mem_heap, heap, 0);
+ ss = strstr ((char *) s, "used:");
+ if (ss)
+ sscanf (ss, "used: %f", &used);
+ else
+ clib_warning ("substring 'used:' not found from show memory");
+ vec_free (s);
+ return (used);
+}
+
+static int
+session_test_enable_disable (vlib_main_t *vm, unformat_input_t *input)
+{
+ u32 iteration = 100, i;
+ uword was_enabled;
+ f32 was_using, now_using;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "repeat %d", &iteration))
+ ;
+ else
+ {
+ vlib_cli_output (vm, "parse error: '%U'", format_unformat_error,
+ input);
+ return -1;
+ }
+ }
+
+ was_enabled = clib_mem_trace_enable_disable (0);
+ /* warm up */
+ for (i = 0; i < 10; i++)
+ {
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_sdl_engine (vm);
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_rule_table_engine (vm);
+ }
+ was_using = session_get_memory_usage ();
+
+ for (i = 0; i < iteration; i++)
+ {
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_sdl_engine (vm);
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_rule_table_engine (vm);
+ }
+ now_using = session_get_memory_usage ();
+
+ clib_mem_trace_enable_disable (was_enabled);
+ SESSION_TEST ((was_using == now_using), "was using %.2fM, now using %.2fM",
+ was_using, now_using);
+
+ return 0;
+}
+
+static int
+session_test_sdl (vlib_main_t *vm, unformat_input_t *input)
+{
+ session_table_t *st = session_table_alloc ();
+ u16 lcl_port = 0, rmt_port = 0;
+ u32 action_index = 1, res;
+ int verbose = 0, error;
+ ip4_address_t lcl_ip;
+ const char ip_str_1234[] = "1.2.3.4";
+ inet_pton (AF_INET, ip_str_1234, &lcl_ip);
+ ip4_address_t rmt_ip = {
+ .as_u32 = clib_host_to_net_u32 (0x0),
+ };
+ ip6_address_t rmt_ip6 = {
+ .as_u64 = { 0, 0 },
+ };
+ fib_prefix_t lcl_pref = {
+ .fp_addr.ip4.as_u32 = lcl_ip.as_u32,
+ .fp_len = 16,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ };
+ fib_prefix_t rmt_pref = {
+ .fp_addr.ip4.as_u32 = rmt_ip.as_u32,
+ .fp_len = 0,
+ .fp_proto = 0,
+ };
+ session_rule_table_add_del_args_t args = {
+ .lcl = lcl_pref,
+ .rmt = rmt_pref,
+ .lcl_port = lcl_port,
+ .rmt_port = rmt_port,
+ .action_index = action_index++,
+ .is_add = 1,
+ };
+ const char ip_str_1200[] = "1.2.0.0";
+ const char ip_str_1230[] = "1.2.3.0";
+ const char ip_str_1111[] = "1.1.1.1";
+ const char ip6_str[] = "2501:0db8:85a3:0000:0000:8a2e:0371:1";
+ const char ip6_str2[] = "2501:0db8:85a3:0000:0000:8a2e:0372:1";
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "verbose"))
+ verbose = 1;
+ else
+ {
+ vlib_cli_output (vm, "parse error: '%U'", format_unformat_error,
+ input);
+ return -1;
+ }
+ }
+
+ session_test_disable_rt_backend_engine (vm);
+ session_test_enable_sdl_engine (vm);
+
+ session_table_init (st, FIB_PROTOCOL_MAX);
+ session_rules_table_init (st, FIB_PROTOCOL_MAX);
+
+ /* Add 1.2.0.0/16 */
+ args.lcl.fp_len = 16;
+ inet_pton (AF_INET, ip_str_1200, &args.lcl.fp_addr.ip4.as_u32);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == 0), "Add %s/%d action %d", ip_str_1200,
+ args.lcl.fp_len, action_index - 1);
+
+ /* Lookup 1.2.3.4 */
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ SESSION_TEST ((res == action_index - 1),
+ "Lookup %s, action should "
+ "be 1: %d",
+ ip_str_1234, action_index - 1);
+
+ /*
+ * Add 1.2.3.0/24
+ */
+ args.lcl.fp_len = 24;
+ inet_pton (AF_INET, ip_str_1230, &args.lcl.fp_addr.ip4.as_u32);
+ args.action_index = action_index++;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == 0), "Add %s/%d action %d", ip_str_1230,
+ args.lcl.fp_len, action_index - 1);
+
+ /* Lookup 1.2.3.4 */
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ SESSION_TEST ((res == action_index - 1),
+ "Lookup %s, action should "
+ "be 2: %d",
+ ip_str_1234, action_index - 1);
+
+ /* look up 1.1.1.1, should be -1 (invalid index) */
+ inet_pton (AF_INET, ip_str_1111, &lcl_ip);
+ res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_ip, &rmt_ip, lcl_port, rmt_port);
+ SESSION_TEST ((res == SESSION_TABLE_INVALID_INDEX),
+ "Lookup %s, action should "
+ "be -1: %d",
+ ip_str_1111, res);
+
+ /* Add again 1.2.0.0/16, should be rejected */
+ args.lcl.fp_len = 16;
+ inet_pton (AF_INET, ip_str_1200, &args.lcl.fp_addr.ip4.as_u32);
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == SESSION_E_IPINUSE), "Add %s/%d action %d",
+ ip_str_1200, args.lcl.fp_len, error);
+ /*
+ * Add 0.0.0.0/0, should get an error
+ */
+ args.lcl.fp_len = 0;
+ args.lcl.fp_addr.ip4.as_u32 = 0;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == SESSION_E_IPINUSE), "Add 0.0.0.0/%d action %d",
+ args.lcl.fp_len, error);
+
+ /* delete 0.0.0.0 should be rejected */
+ args.is_add = 0;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == SESSION_E_NOROUTE), "Del 0.0.0.0/%d action %d",
+ args.lcl.fp_len, error);
+ if (verbose)
+ session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP,
+ FIB_PROTOCOL_IP4);
+
+ /*
+ * Clean up
+ * Delete 1.2.0.0/16
+ * Delete 1.2.3.0/24
+ */
+ inet_pton (AF_INET, ip_str_1200, &args.lcl.fp_addr.ip4.as_u32);
+ args.lcl.fp_len = 16;
+ args.is_add = 0;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == 0), "Del %s/%d should 0: %d", ip_str_1200,
+ args.lcl.fp_len, error);
+
+ inet_pton (AF_INET, ip_str_1230, &args.lcl.fp_addr.ip4.as_u32);
+ args.lcl.fp_len = 24;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == 0), "Del %s/%d, should be 0: %d", ip_str_1230,
+ args.lcl.fp_len, error);
+ if (verbose)
+ session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP,
+ FIB_PROTOCOL_IP4);
+
+ /* ip6 tests */
+
+ /*
+ * Add ip6 2001:0db8:85a3:0000:0000:8a2e:0371:1/124
+ */
+ ip6_address_t lcl_lkup;
+ inet_pton (AF_INET6, ip6_str, &args.lcl.fp_addr.ip6);
+ args.lcl.fp_len = 124;
+ args.lcl.fp_proto = FIB_PROTOCOL_IP6;
+ args.action_index = action_index++;
+ args.is_add = 1;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == 0), "Add %s/%d action %d", ip6_str, args.lcl.fp_len,
+ action_index - 1);
+ if (verbose)
+ session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP,
+ FIB_PROTOCOL_IP6);
+
+ /* Lookup 2001:0db8:85a3:0000:0000:8a2e:0371:1 */
+ res = session_rules_table_lookup6 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &args.lcl.fp_addr.ip6, &rmt_ip6, lcl_port,
+ rmt_port);
+ SESSION_TEST ((res == action_index - 1),
+ "Lookup %s action should "
+ "be 3: %d",
+ ip6_str, action_index - 1);
+
+ /* Lookup 2001:0db8:85a3:0000:0000:8a2e:0372:1 */
+ inet_pton (AF_INET6, ip6_str2, &lcl_lkup);
+ res = session_rules_table_lookup6 (st->srtg_handle, TRANSPORT_PROTO_TCP,
+ &lcl_lkup, &rmt_ip6, lcl_port, rmt_port);
+ SESSION_TEST ((res == SESSION_TABLE_INVALID_INDEX),
+ "Lookup %s action should "
+ "be -1: %d",
+ ip6_str2, res);
+
+ /*
+ * del ip6 2001:0db8:85a3:0000:0000:8a2e:0371:1/124
+ */
+ args.is_add = 0;
+ args.lcl.fp_len = 124;
+ error =
+ session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args);
+ SESSION_TEST ((error == 0), "del %s/%d, should be 0: %d", ip6_str,
+ args.lcl.fp_len, error);
+ if (verbose)
+ session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP,
+ FIB_PROTOCOL_IP6);
+
+ session_table_free (st, FIB_PROTOCOL_MAX);
+
+ return 0;
+}
+
static clib_error_t *
session_test (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd_arg)
{
int res = 0;
- vnet_session_enable_disable (vm, 1);
+ session_test_enable_rule_table_engine (vm);
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
@@ -2099,6 +2415,10 @@ session_test (vlib_main_t * vm,
res = session_test_mq_speed (vm, input);
else if (unformat (input, "mq-basic"))
res = session_test_mq_basic (vm, input);
+ else if (unformat (input, "enable-disable"))
+ res = session_test_enable_disable (vm, input);
+ else if (unformat (input, "sdl"))
+ res = session_test_sdl (vm, input);
else if (unformat (input, "all"))
{
if ((res = session_test_basic (vm, input)))
@@ -2117,6 +2437,10 @@ session_test (vlib_main_t * vm,
goto done;
if ((res = session_test_mq_basic (vm, input)))
goto done;
+ if ((res = session_test_sdl (vm, input)))
+ goto done;
+ if ((res = session_test_enable_disable (vm, input)))
+ goto done;
}
else
break;
diff --git a/src/plugins/unittest/tcp_test.c b/src/plugins/unittest/tcp_test.c
index 34033a0b622..bd39474ce93 100644
--- a/src/plugins/unittest/tcp_test.c
+++ b/src/plugins/unittest/tcp_test.c
@@ -1550,8 +1550,11 @@ tcp_test (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd_arg)
{
int res = 0;
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
- vnet_session_enable_disable (vm, 1);
+ vnet_session_enable_disable (vm, &args);
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
diff --git a/src/plugins/unittest/util_test.c b/src/plugins/unittest/util_test.c
index 53384e55494..5b7e30bc21f 100644
--- a/src/plugins/unittest/util_test.c
+++ b/src/plugins/unittest/util_test.c
@@ -101,6 +101,36 @@ VLIB_CLI_COMMAND (test_hash_command, static) =
.function = test_hash_command_fn,
};
+static void *
+leak_memory_fn (void *args)
+{
+ u8 *p = 0;
+ vec_validate (p, 100);
+ p = 0;
+ return 0;
+}
+
+static clib_error_t *
+test_mem_leak_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ /* do memory leak from thread, so no 'unix_cli' in traceback */
+ pthread_t thread;
+ int rv = pthread_create (&thread, NULL, leak_memory_fn, 0);
+ if (rv)
+ {
+ return clib_error_return (0, "pthread_create failed");
+ }
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (test_mem_leak_command, static) = {
+ .path = "test mem-leak",
+ .short_help = "leak some memory",
+ .function = test_mem_leak_command_fn,
+};
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/plugins/urpf/CMakeLists.txt b/src/plugins/urpf/CMakeLists.txt
index 2f44e3b2344..f665d30b0bb 100644
--- a/src/plugins/urpf/CMakeLists.txt
+++ b/src/plugins/urpf/CMakeLists.txt
@@ -22,6 +22,10 @@ add_vpp_plugin(urpf
ip4_urpf.c
ip6_urpf.c
+ INSTALL_HEADERS
+ urpf_dp.h
+ urpf.h
+
API_FILES
urpf.api
)
diff --git a/src/plugins/urpf/urpf.c b/src/plugins/urpf/urpf.c
index e5209caafb4..1e7d6c0fb91 100644
--- a/src/plugins/urpf/urpf.c
+++ b/src/plugins/urpf/urpf.c
@@ -60,7 +60,17 @@ static const char *urpf_feats[N_AF][VLIB_N_DIR][URPF_N_MODES] =
urpf_data_t *urpf_cfgs[N_AF][VLIB_N_DIR];
u8 *
-format_urpf_mode (u8 * s, va_list * a)
+format_urpf_trace (u8 *s, va_list *va)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
+ urpf_trace_t *t = va_arg (*va, urpf_trace_t *);
+
+ return format (s, "uRPF:%d fib:%d", t->urpf, t->fib_index);
+}
+
+__clib_export u8 *
+format_urpf_mode (u8 *s, va_list *a)
{
urpf_mode_t mode = va_arg (*a, int);
@@ -76,8 +86,8 @@ format_urpf_mode (u8 * s, va_list * a)
return (format (s, "unknown"));
}
-static uword
-unformat_urpf_mode (unformat_input_t * input, va_list * args)
+__clib_export uword
+unformat_urpf_mode (unformat_input_t *input, va_list *args)
{
urpf_mode_t *mode = va_arg (*args, urpf_mode_t *);
@@ -94,7 +104,16 @@ unformat_urpf_mode (unformat_input_t * input, va_list * args)
return 0;
}
-int
+__clib_export int
+urpf_feature_enable_disable (ip_address_family_t af, vlib_dir_t dir,
+ urpf_mode_t mode, u32 sw_if_index, int enable)
+{
+ return vnet_feature_enable_disable (urpf_feat_arcs[af][dir],
+ urpf_feats[af][dir][mode], sw_if_index,
+ enable, 0, 0);
+}
+
+__clib_export int
urpf_update (urpf_mode_t mode, u32 sw_if_index, ip_address_family_t af,
vlib_dir_t dir, u32 table_id)
{
diff --git a/src/plugins/urpf/urpf.h b/src/plugins/urpf/urpf.h
index 6983a2b440c..a40a25df16b 100644
--- a/src/plugins/urpf/urpf.h
+++ b/src/plugins/urpf/urpf.h
@@ -32,7 +32,15 @@ typedef enum urpf_mode_t_
#define URPF_N_MODES (URPF_MODE_STRICT+1)
-extern u8 *format_urpf_mode (u8 * s, va_list * a);
+typedef struct
+{
+ index_t urpf;
+ u32 fib_index;
+} urpf_trace_t;
+
+u8 *format_urpf_trace (u8 *s, va_list *va);
+u8 *format_urpf_mode (u8 *s, va_list *a);
+uword unformat_urpf_mode (unformat_input_t *input, va_list *args);
typedef struct
{
@@ -43,8 +51,8 @@ typedef struct
extern urpf_data_t *urpf_cfgs[N_AF][VLIB_N_DIR];
-extern int urpf_update (urpf_mode_t mode, u32 sw_if_index,
- ip_address_family_t af, vlib_dir_t dir, u32 table_id);
+int urpf_update (urpf_mode_t mode, u32 sw_if_index, ip_address_family_t af,
+ vlib_dir_t dir, u32 table_id);
#endif
diff --git a/src/plugins/urpf/urpf_dp.h b/src/plugins/urpf/urpf_dp.h
index 816d8b70b90..b17fed7e04b 100644
--- a/src/plugins/urpf/urpf_dp.h
+++ b/src/plugins/urpf/urpf_dp.h
@@ -53,22 +53,6 @@
*
* This file contains the interface unicast source check.
*/
-typedef struct
-{
- index_t urpf;
-} urpf_trace_t;
-
-static u8 *
-format_urpf_trace (u8 * s, va_list * va)
-{
- CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
- CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
- urpf_trace_t *t = va_arg (*va, urpf_trace_t *);
-
- s = format (s, "uRPF:%d", t->urpf);
-
- return s;
-}
#define foreach_urpf_error \
_(DROP, "uRPF Drop") \
@@ -87,10 +71,157 @@ typedef enum
URPF_N_NEXT,
} urpf_next_t;
+static_always_inline u32
+urpf_get_fib_index (vlib_buffer_t *b, ip_address_family_t af, vlib_dir_t dir)
+{
+ u32 sw_if_index = vnet_buffer (b)->sw_if_index[dir];
+ return vec_elt (urpf_cfgs[af][dir], sw_if_index).fib_index;
+}
+
+static_always_inline void
+urpf_perform_check_x1 (ip_address_family_t af, vlib_dir_t dir,
+ urpf_mode_t mode, vlib_buffer_t *b, const u8 *h,
+ u32 fib_index, load_balance_t **lb, u32 *pass)
+{
+ load_balance_t *llb;
+ u32 lpass;
+ u32 lb_index;
+
+ ASSERT (fib_index != ~0);
+
+ if (AF_IP4 == af)
+ {
+ const ip4_header_t *ip;
+
+ ip = (ip4_header_t *) h;
+
+ lb_index = ip4_fib_forwarding_lookup (fib_index, &ip->src_address);
+
+ /* Pass multicast. */
+ lpass = (ip4_address_is_multicast (&ip->src_address) ||
+ ip4_address_is_global_broadcast (&ip->src_address));
+ }
+ else
+ {
+ const ip6_header_t *ip;
+
+ ip = (ip6_header_t *) h;
+
+ lb_index = ip6_fib_table_fwding_lookup (fib_index, &ip->src_address);
+ lpass = ip6_address_is_multicast (&ip->src_address);
+ }
+
+ llb = load_balance_get (lb_index);
+
+ if (URPF_MODE_STRICT == mode)
+ {
+ int res;
+
+ res = fib_urpf_check (llb->lb_urpf, vnet_buffer (b)->sw_if_index[dir]);
+ if (VLIB_RX == dir)
+ lpass |= res;
+ else
+ {
+ lpass |= !res && fib_urpf_check_size (llb->lb_urpf);
+ lpass |= b->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
+ }
+ }
+ else
+ lpass |= fib_urpf_check_size (llb->lb_urpf);
+
+ *lb = llb;
+ *pass = lpass;
+}
+
+static_always_inline void
+urpf_perform_check_x2 (ip_address_family_t af, vlib_dir_t dir,
+ urpf_mode_t mode, vlib_buffer_t *b0, vlib_buffer_t *b1,
+ const u8 *h0, const u8 *h1, u32 fib_index0,
+ u32 fib_index1, load_balance_t **lb0,
+ load_balance_t **lb1, u32 *pass0, u32 *pass1)
+{
+ load_balance_t *llb0, *llb1;
+ u32 lpass0, lpass1;
+ u32 lb_index0, lb_index1;
+
+ ASSERT (fib_index0 != ~0);
+ ASSERT (fib_index1 != ~0);
+
+ if (AF_IP4 == af)
+ {
+ const ip4_header_t *ip0, *ip1;
+
+ ip0 = (ip4_header_t *) h0;
+ ip1 = (ip4_header_t *) h1;
+
+ ip4_fib_forwarding_lookup_x2 (fib_index0, fib_index1, &ip0->src_address,
+ &ip1->src_address, &lb_index0, &lb_index1);
+ /* Pass multicast. */
+ lpass0 = (ip4_address_is_multicast (&ip0->src_address) ||
+ ip4_address_is_global_broadcast (&ip0->src_address));
+ lpass1 = (ip4_address_is_multicast (&ip1->src_address) ||
+ ip4_address_is_global_broadcast (&ip1->src_address));
+ }
+ else
+ {
+ const ip6_header_t *ip0, *ip1;
+
+ ip0 = (ip6_header_t *) h0;
+ ip1 = (ip6_header_t *) h1;
+
+ lb_index0 = ip6_fib_table_fwding_lookup (fib_index0, &ip0->src_address);
+ lb_index1 = ip6_fib_table_fwding_lookup (fib_index1, &ip1->src_address);
+ lpass0 = ip6_address_is_multicast (&ip0->src_address);
+ lpass1 = ip6_address_is_multicast (&ip1->src_address);
+ }
+
+ llb0 = load_balance_get (lb_index0);
+ llb1 = load_balance_get (lb_index1);
+
+ if (URPF_MODE_STRICT == mode)
+ {
+ /* for RX the check is: would this source adddress be
+ * forwarded out of the interface on which it was recieved,
+ * if yes allow. For TX it's; would this source address be
+ * forwarded out of the interface through which it is being
+ * sent, if yes drop.
+ */
+ int res0, res1;
+
+ res0 =
+ fib_urpf_check (llb0->lb_urpf, vnet_buffer (b0)->sw_if_index[dir]);
+ res1 =
+ fib_urpf_check (llb1->lb_urpf, vnet_buffer (b1)->sw_if_index[dir]);
+
+ if (VLIB_RX == dir)
+ {
+ lpass0 |= res0;
+ lpass1 |= res1;
+ }
+ else
+ {
+ lpass0 |= !res0 && fib_urpf_check_size (llb0->lb_urpf);
+ lpass1 |= !res1 && fib_urpf_check_size (llb1->lb_urpf);
+
+ /* allow locally generated */
+ lpass0 |= b0->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
+ lpass1 |= b1->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
+ }
+ }
+ else
+ {
+ lpass0 |= fib_urpf_check_size (llb0->lb_urpf);
+ lpass1 |= fib_urpf_check_size (llb1->lb_urpf);
+ }
+
+ *lb0 = llb0;
+ *lb1 = llb1;
+ *pass0 = lpass0;
+ *pass1 = lpass1;
+}
+
static_always_inline uword
-urpf_inline (vlib_main_t * vm,
- vlib_node_runtime_t * node,
- vlib_frame_t * frame,
+urpf_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
ip_address_family_t af, vlib_dir_t dir, urpf_mode_t mode)
{
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
@@ -106,8 +237,8 @@ urpf_inline (vlib_main_t * vm,
while (n_left >= 4)
{
- u32 pass0, lb_index0, pass1, lb_index1;
- const load_balance_t *lb0, *lb1;
+ u32 pass0, pass1;
+ load_balance_t *lb0 = 0, *lb1 = 0;
u32 fib_index0, fib_index1;
const u8 *h0, *h1;
@@ -121,87 +252,32 @@ urpf_inline (vlib_main_t * vm,
h0 = (u8 *) vlib_buffer_get_current (b[0]);
h1 = (u8 *) vlib_buffer_get_current (b[1]);
-
if (VLIB_TX == dir)
{
h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
h1 += vnet_buffer (b[1])->ip.save_rewrite_length;
}
- fib_index0 =
- urpf_cfgs[af][dir][vnet_buffer (b[0])->sw_if_index[dir]].fib_index;
- fib_index1 =
- urpf_cfgs[af][dir][vnet_buffer (b[1])->sw_if_index[dir]].fib_index;
+ fib_index0 = urpf_get_fib_index (b[0], af, dir);
+ fib_index1 = urpf_get_fib_index (b[1], af, dir);
+ urpf_perform_check_x2 (af, dir, mode, b[0], b[1], h0, h1, fib_index0,
+ fib_index1, &lb0, &lb1, &pass0, &pass1);
- if (AF_IP4 == af)
- {
- const ip4_header_t *ip0, *ip1;
-
- ip0 = (ip4_header_t *) h0;
- ip1 = (ip4_header_t *) h1;
-
- ip4_fib_forwarding_lookup_x2 (fib_index0,
- fib_index1,
- &ip0->src_address,
- &ip1->src_address,
- &lb_index0, &lb_index1);
- /* Pass multicast. */
- pass0 = (ip4_address_is_multicast (&ip0->src_address) ||
- ip4_address_is_global_broadcast (&ip0->src_address));
- pass1 = (ip4_address_is_multicast (&ip1->src_address) ||
- ip4_address_is_global_broadcast (&ip1->src_address));
- }
- else
+ if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
{
- const ip6_header_t *ip0, *ip1;
-
- ip0 = (ip6_header_t *) h0;
- ip1 = (ip6_header_t *) h1;
-
- lb_index0 = ip6_fib_table_fwding_lookup (fib_index0,
- &ip0->src_address);
- lb_index1 = ip6_fib_table_fwding_lookup (fib_index1,
- &ip1->src_address);
- pass0 = ip6_address_is_multicast (&ip0->src_address);
- pass1 = ip6_address_is_multicast (&ip1->src_address);
- }
-
- lb0 = load_balance_get (lb_index0);
- lb1 = load_balance_get (lb_index1);
+ urpf_trace_t *t;
- if (URPF_MODE_STRICT == mode)
- {
- /* for RX the check is: would this source adddress be forwarded
- * out of the interface on which it was recieved, if yes allow.
- * For TX it's; would this source address be forwarded out of the
- * interface through which it is being sent, if yes drop.
- */
- int res0, res1;
-
- res0 = fib_urpf_check (lb0->lb_urpf,
- vnet_buffer (b[0])->sw_if_index[dir]);
- res1 = fib_urpf_check (lb1->lb_urpf,
- vnet_buffer (b[1])->sw_if_index[dir]);
-
- if (VLIB_RX == dir)
- {
- pass0 |= res0;
- pass1 |= res1;
- }
- else
- {
- pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf);
- pass1 |= !res1 && fib_urpf_check_size (lb1->lb_urpf);
-
- /* allow locally generated */
- pass0 |= b[0]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
- pass1 |= b[1]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
- }
+ t = vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->urpf = lb0 ? lb0->lb_urpf : ~0;
+ t->fib_index = fib_index0;
}
- else
+ if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
{
- pass0 |= fib_urpf_check_size (lb0->lb_urpf);
- pass1 |= fib_urpf_check_size (lb1->lb_urpf);
+ urpf_trace_t *t;
+
+ t = vlib_add_trace (vm, node, b[1], sizeof (*t));
+ t->urpf = lb1 ? lb1->lb_urpf : ~0;
+ t->fib_index = fib_index1;
}
if (PREDICT_TRUE (pass0))
@@ -218,22 +294,6 @@ urpf_inline (vlib_main_t * vm,
next[1] = URPF_NEXT_DROP;
b[1]->error = node->errors[URPF_ERROR_DROP];
}
-
- if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
- {
- urpf_trace_t *t;
-
- t = vlib_add_trace (vm, node, b[0], sizeof (*t));
- t->urpf = lb0->lb_urpf;
- }
- if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
- {
- urpf_trace_t *t;
-
- t = vlib_add_trace (vm, node, b[1], sizeof (*t));
- t->urpf = lb1->lb_urpf;
- }
-
b += 2;
next += 2;
n_left -= 2;
@@ -241,8 +301,8 @@ urpf_inline (vlib_main_t * vm,
while (n_left)
{
- u32 pass0, lb_index0, fib_index0;
- const load_balance_t *lb0;
+ u32 pass0, fib_index0;
+ load_balance_t *lb0 = 0;
const u8 *h0;
h0 = (u8 *) vlib_buffer_get_current (b[0]);
@@ -250,51 +310,18 @@ urpf_inline (vlib_main_t * vm,
if (VLIB_TX == dir)
h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
- fib_index0 =
- urpf_cfgs[af][dir][vnet_buffer (b[0])->sw_if_index[dir]].fib_index;
-
- if (AF_IP4 == af)
- {
- const ip4_header_t *ip0;
-
- ip0 = (ip4_header_t *) h0;
-
- lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
- &ip0->src_address);
+ fib_index0 = urpf_get_fib_index (b[0], af, dir);
+ urpf_perform_check_x1 (af, dir, mode, b[0], h0, fib_index0, &lb0,
+ &pass0);
- /* Pass multicast. */
- pass0 = (ip4_address_is_multicast (&ip0->src_address) ||
- ip4_address_is_global_broadcast (&ip0->src_address));
- }
- else
+ if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
{
- const ip6_header_t *ip0;
-
- ip0 = (ip6_header_t *) h0;
-
- lb_index0 = ip6_fib_table_fwding_lookup (fib_index0,
- &ip0->src_address);
- pass0 = ip6_address_is_multicast (&ip0->src_address);
- }
-
- lb0 = load_balance_get (lb_index0);
+ urpf_trace_t *t;
- if (URPF_MODE_STRICT == mode)
- {
- int res0;
-
- res0 = fib_urpf_check (lb0->lb_urpf,
- vnet_buffer (b[0])->sw_if_index[dir]);
- if (VLIB_RX == dir)
- pass0 |= res0;
- else
- {
- pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf);
- pass0 |= b[0]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
- }
+ t = vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->urpf = lb0 ? lb0->lb_urpf : ~0;
+ t->fib_index = fib_index0;
}
- else
- pass0 |= fib_urpf_check_size (lb0->lb_urpf);
if (PREDICT_TRUE (pass0))
vnet_feature_next_u16 (&next[0], b[0]);
@@ -303,14 +330,6 @@ urpf_inline (vlib_main_t * vm,
next[0] = URPF_NEXT_DROP;
b[0]->error = node->errors[URPF_ERROR_DROP];
}
-
- if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
- {
- urpf_trace_t *t;
-
- t = vlib_add_trace (vm, node, b[0], sizeof (*t));
- t->urpf = lb0->lb_urpf;
- }
b++;
next++;
n_left--;
diff --git a/src/plugins/wireguard/wireguard_chachapoly.c b/src/plugins/wireguard/wireguard_chachapoly.c
index 0dd7908d2e2..ad644ff6cb8 100644
--- a/src/plugins/wireguard/wireguard_chachapoly.c
+++ b/src/plugins/wireguard/wireguard_chachapoly.c
@@ -72,11 +72,11 @@ wg_xchacha20poly1305_encrypt (vlib_main_t *vm, u8 *src, u32 src_len, u8 *dst,
u64 h_nonce;
clib_memcpy (&h_nonce, nonce + 16, sizeof (h_nonce));
- h_nonce = le64toh (h_nonce);
+ h_nonce = clib_little_to_host_u64 (h_nonce);
hchacha20 (derived_key, nonce, key);
for (i = 0; i < (sizeof (derived_key) / sizeof (derived_key[0])); i++)
- (derived_key[i]) = htole32 ((derived_key[i]));
+ (derived_key[i]) = clib_host_to_little_u32 ((derived_key[i]));
uint32_t key_idx;
@@ -102,11 +102,11 @@ wg_xchacha20poly1305_decrypt (vlib_main_t *vm, u8 *src, u32 src_len, u8 *dst,
u64 h_nonce;
clib_memcpy (&h_nonce, nonce + 16, sizeof (h_nonce));
- h_nonce = le64toh (h_nonce);
+ h_nonce = clib_little_to_host_u64 (h_nonce);
hchacha20 (derived_key, nonce, key);
for (i = 0; i < (sizeof (derived_key) / sizeof (derived_key[0])); i++)
- (derived_key[i]) = htole32 ((derived_key[i]));
+ (derived_key[i]) = clib_host_to_little_u32 ((derived_key[i]));
uint32_t key_idx;
diff --git a/src/plugins/wireguard/wireguard_noise.c b/src/plugins/wireguard/wireguard_noise.c
index 5fe2e44b03b..c3f28f442f5 100644
--- a/src/plugins/wireguard/wireguard_noise.c
+++ b/src/plugins/wireguard/wireguard_noise.c
@@ -751,8 +751,8 @@ noise_tai64n_now (uint8_t output[NOISE_TIMESTAMP_LEN])
unix_nanosec &= REJECT_INTERVAL_MASK;
/* https://cr.yp.to/libtai/tai64.html */
- sec = htobe64 (0x400000000000000aULL + unix_sec);
- nsec = htobe32 (unix_nanosec);
+ sec = clib_host_to_big_u64 (0x400000000000000aULL + unix_sec);
+ nsec = clib_host_to_big_u32 (unix_nanosec);
/* memcpy to output buffer, assuming output could be unaligned. */
clib_memcpy (output, &sec, sizeof (sec));
diff --git a/src/svm/svmdb.c b/src/svm/svmdb.c
index 3c69dbf45ba..98bb96f3dd4 100644
--- a/src/svm/svmdb.c
+++ b/src/svm/svmdb.c
@@ -436,7 +436,7 @@ svmdb_local_serialize_strings (svmdb_client_t * client, char *filename)
u8 *sanitized_name = 0;
int fd = 0;
- if (strstr (filename, "..") || index (filename, '/'))
+ if (strstr (filename, "..") || strchr (filename, '/'))
{
error = clib_error_return (0, "Illegal characters in filename '%s'",
filename);
diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py
index 2b0ce9999d7..0bed578eb93 100755
--- a/src/tools/vppapigen/vppapigen.py
+++ b/src/tools/vppapigen/vppapigen.py
@@ -9,6 +9,7 @@ import os
from subprocess import Popen, PIPE
import ply.lex as lex
import ply.yacc as yacc
+from io import TextIOWrapper
assert sys.version_info >= (3, 5), "Not supported Python version: {}".format(
sys.version
@@ -1166,6 +1167,21 @@ def foldup_crcs(s):
f.crc = foldup_blocks(f.block, binascii.crc32(f.crc) & 0xFFFFFFFF)
+def write_dependencies(output_file, dependency_file, imports):
+ r = []
+ for i in imports:
+ for d in dirlist:
+ f = os.path.abspath(os.path.join(d, i.filename))
+ if os.path.exists(f):
+ r.append(f)
+ with open(dependency_file, "w", encoding="utf8") as f:
+ print(f"{output_file}: \\", file=f)
+ for i in r[:-1]:
+ print(f" {i} \\", file=f)
+ if imports:
+ print(f" {r[-1]}", file=f)
+
+
def run_vppapigen(
input_file=None,
output=sys.stdout,
@@ -1176,6 +1192,7 @@ def run_vppapigen(
outputdir=None,
pluginpath="",
git_revision=None,
+ dependency_file=None,
):
# reset globals
dirlist.clear()
@@ -1256,6 +1273,9 @@ def run_vppapigen(
imports = parser.process_imports(parsed_objects, False, result)
s["imported"] = parser.process(imports)
+ if dependency_file and isinstance(output, TextIOWrapper):
+ write_dependencies(output.name, dependency_file[0], s["Import"])
+
# Add msg_id field
s["Define"] = add_msg_id(s["Define"])
@@ -1324,6 +1344,7 @@ def main():
cliparser.add_argument(
"--git-revision", help="Git revision to use for opening files"
)
+ cliparser.add_argument("-MF", nargs=1, help="Dependency file")
args = cliparser.parse_args()
return run_vppapigen(
@@ -1336,6 +1357,7 @@ def main():
pluginpath=args.pluginpath,
git_revision=args.git_revision,
output=args.output,
+ dependency_file=args.MF,
)
diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py
index c2e1e7da7b7..3cd52df1ad0 100755
--- a/src/tools/vppapigen/vppapigen_c.py
+++ b/src/tools/vppapigen/vppapigen_c.py
@@ -171,14 +171,10 @@ class ToJSON:
write(" {\n")
# What is length field doing here?
write(
- ' u8 *s = format(0, "0x%U", format_hex_bytes, '
+ ' char *s = format_c_string(0, "0x%U", format_hex_bytes_no_wrap, '
"&a->{n}, {lfield});\n".format(n=o.fieldname, lfield=lfield)
)
- write(
- ' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'.format(
- n=o.fieldname
- )
- )
+ write(' cJSON_AddStringToObject(o, "{n}", s);\n'.format(n=o.fieldname))
write(" vec_free(s);\n")
write(" }\n")
return
@@ -275,8 +271,12 @@ class ToJSON:
"(vl_api_{name}_t *a) {{\n".format(name=o.name)
)
- write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'.format(o.name))
- write(" cJSON *o = cJSON_CreateString((char *)s);\n")
+ write(
+ ' char *s = format_c_string(0, "%U", format_vl_api_{}_t, a);\n'.format(
+ o.name
+ )
+ )
+ write(" cJSON *o = cJSON_CreateString(s);\n")
write(" vec_free(s);\n")
write(" return o;\n")
write("}\n")
@@ -1143,12 +1143,20 @@ ENDIAN_STRINGS = {
}
-def get_endian_string(o, fieldtype):
+def get_endian_string(fieldtype):
"""Return proper endian string conversion function"""
return ENDIAN_STRINGS[fieldtype]
-def endianfun_array(o):
+def get_lengthfield_type(fieldname, block):
+ """Return the type of the length field"""
+ for o in block:
+ if o.fieldname == fieldname:
+ return o.fieldtype
+ return None
+
+
+def endianfun_array(o, block):
"""Generate endian functions for arrays"""
forloop = """\
ASSERT((u32){length} <= (u32)VL_API_MAX_ARRAY_SIZE);
@@ -1167,12 +1175,16 @@ def endianfun_array(o):
if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool":
output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
else:
- lfield = "a->" + o.lengthfield if o.lengthfield else o.length
+ # lfield = "a->" + o.lengthfield if o.lengthfield else o.length
if o.lengthfield:
- output += (
- f" u32 count = to_net ? clib_host_to_net_u32(a->{o.lengthfield}) : "
- f"a->{o.lengthfield};\n"
- )
+ fieldtype = get_lengthfield_type(o.lengthfield, block)
+ if fieldtype == "u8":
+ output += f" u32 count = a->{o.lengthfield};\n"
+ else:
+ output += (
+ f" u32 count = to_net ? {get_endian_string(fieldtype)}(a->{o.lengthfield}) : "
+ f"a->{o.lengthfield};\n"
+ )
lfield = "count"
else:
lfield = o.length
@@ -1180,7 +1192,7 @@ def endianfun_array(o):
if o.fieldtype in ENDIAN_STRINGS:
output += forloop.format(
length=lfield,
- format=get_endian_string(o, o.fieldtype),
+ format=get_endian_string(o.fieldtype),
name=o.fieldname,
)
else:
@@ -1193,11 +1205,11 @@ def endianfun_array(o):
NO_ENDIAN_CONVERSION = {"client_index": None}
-def endianfun_obj(o):
+def endianfun_obj(o, block):
"""Generate endian conversion function for type"""
output = ""
if o.type == "Array":
- return endianfun_array(o)
+ return endianfun_array(o, block)
if o.type != "Field":
output += ' s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
o.type, o.fieldtype, o.fieldname
@@ -1208,7 +1220,7 @@ def endianfun_obj(o):
return output
if o.fieldtype in ENDIAN_STRINGS:
output += " a->{name} = {format}(a->{name});\n".format(
- name=o.fieldname, format=get_endian_string(o, o.fieldtype)
+ name=o.fieldname, format=get_endian_string(o.fieldtype)
)
elif o.fieldtype.startswith("vl_api_"):
output += " {type}_endian(&a->{name}, to_net);\n".format(
@@ -1252,7 +1264,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a, bool to_net)
if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
output += signature.format(name=t.name)
if t.enumtype in ENDIAN_STRINGS:
- output += " *a = {}(*a);\n".format(get_endian_string(t, t.enumtype))
+ output += " *a = {}(*a);\n".format(get_endian_string(t.enumtype))
else:
output += " /* a->{name} = a->{name} (no-op) */\n".format(
name=t.name
@@ -1273,7 +1285,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a, bool to_net)
)
elif t.alias["type"] in FORMAT_STRINGS:
output += " *a = {}(*a);\n".format(
- get_endian_string(t, t.alias["type"])
+ get_endian_string(t.alias["type"])
)
else:
output += " /* Not Implemented yet {} */".format(t.name)
@@ -1283,7 +1295,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a, bool to_net)
output += signature.format(name=t.name)
for o in t.block:
- output += endianfun_obj(o)
+ output += endianfun_obj(o, t.block)
output += "}\n\n"
output += "\n#endif"
@@ -1349,7 +1361,7 @@ static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
)
lf = m[0]
if lf.fieldtype in ENDIAN_STRINGS:
- output += f" + {get_endian_string(b, lf.fieldtype)}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
+ output += f" + {get_endian_string(lf.fieldtype)}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
elif lf.fieldtype == "u8":
output += (
f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index 45ba025f191..48c1ccaa820 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -2151,7 +2151,7 @@ elog_save (vat_main_t * vam)
}
/* It's fairly hard to get "../oopsie" through unformat; just in case */
- if (strstr (file, "..") || index (file, '/'))
+ if (strstr (file, "..") || strchr (file, '/'))
{
errmsg ("illegal characters in filename '%s'", file);
return 0;
diff --git a/src/vcl/vcl_locked.c b/src/vcl/vcl_locked.c
index 69dd15b0ef4..93ece0027ff 100644
--- a/src/vcl/vcl_locked.c
+++ b/src/vcl/vcl_locked.c
@@ -374,6 +374,12 @@ vls_worker_get_current (void)
return pool_elt_at_index (vlsm->workers, vls_get_worker_index ());
}
+static inline u8
+vls_n_workers (void)
+{
+ return pool_elts (vlsm->workers);
+}
+
static void
vls_worker_alloc (void)
{
@@ -1298,7 +1304,7 @@ vls_mp_checks (vcl_locked_session_t * vls, int is_add)
vcl_session_t *s;
u32 owner_wrk;
- if (vls_mt_wrk_supported ())
+ if (vls_mt_wrk_supported () && vls_n_workers () <= 1)
return;
ASSERT (wrk->wrk_index == vls->vcl_wrk_index);
diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c
index a557093e897..2ebb1d8e5b6 100644
--- a/src/vcl/vppcom.c
+++ b/src/vcl/vppcom.c
@@ -1754,6 +1754,10 @@ vppcom_unformat_proto (uint8_t * proto, char *proto_str)
*proto = VPPCOM_PROTO_SRTP;
else if (!strcmp (proto_str, "srtp"))
*proto = VPPCOM_PROTO_SRTP;
+ else if (!strcmp (proto_str, "HTTP"))
+ *proto = VPPCOM_PROTO_HTTP;
+ else if (!strcmp (proto_str, "http"))
+ *proto = VPPCOM_PROTO_HTTP;
else
return 1;
return 0;
@@ -2345,6 +2349,75 @@ vppcom_session_write_inline (vcl_worker_t *wrk, vcl_session_t *s, void *buf,
}
int
+vppcom_session_write_segments (uint32_t session_handle,
+ vppcom_data_segment_t *ds, uint32_t n_segments)
+{
+ vcl_worker_t *wrk = vcl_worker_get_current ();
+ int n_write = 0, n_bytes = 0, is_nonblocking;
+ vcl_session_t *s = 0;
+ svm_fifo_t *tx_fifo;
+ svm_msg_q_t *mq;
+ u8 is_ct;
+ u32 i;
+
+ if (PREDICT_FALSE (!ds))
+ return VPPCOM_EFAULT;
+
+ /* Accept zero length writes but just return */
+ if (PREDICT_FALSE (ds[0].len == 0))
+ return VPPCOM_OK;
+
+ s = vcl_session_get_w_handle (wrk, session_handle);
+ if (PREDICT_FALSE (!s || (s->flags & VCL_SESSION_F_IS_VEP)))
+ return VPPCOM_EBADFD;
+
+ if (PREDICT_FALSE (!vcl_session_is_open (s)))
+ return vcl_session_closed_error (s);
+
+ if (PREDICT_FALSE (s->flags & VCL_SESSION_F_WR_SHUTDOWN))
+ return VPPCOM_EPIPE;
+
+ is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
+ is_ct = vcl_session_is_ct (s);
+ mq = wrk->app_event_queue;
+ tx_fifo = is_ct ? s->ct_tx_fifo : s->tx_fifo;
+
+ for (i = 0; i < n_segments; i++)
+ n_bytes += ds[i].len;
+
+ if (svm_fifo_max_enqueue_prod (tx_fifo) < n_bytes)
+ {
+ if (is_nonblocking)
+ {
+ return VPPCOM_EWOULDBLOCK;
+ }
+ while (svm_fifo_max_enqueue_prod (tx_fifo) < n_bytes)
+ {
+ svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ if (vcl_session_is_closing (s))
+ return vcl_session_closing_error (s);
+
+ svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
+ vcl_worker_flush_mq_events (wrk);
+ }
+ }
+
+ n_write = svm_fifo_enqueue_segments (tx_fifo, (svm_fifo_seg_t *) ds,
+ n_segments, 0 /* allow_partial */);
+
+ /* The underlying fifo segment can run out of memory */
+ if (PREDICT_FALSE (n_write < 0))
+ return VPPCOM_EAGAIN;
+
+ if (svm_fifo_set_event (s->tx_fifo))
+ app_send_io_evt_to_vpp (s->vpp_evt_q,
+ s->tx_fifo->shr->master_session_index,
+ SESSION_IO_EVT_TX, SVM_Q_WAIT);
+
+ return n_write;
+}
+
+int
vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
{
vcl_worker_t *wrk = vcl_worker_get_current ();
@@ -2619,6 +2692,7 @@ vppcom_select_eventfd (vcl_worker_t * wrk, int n_bits,
vcl_mq_evt_conn_t *mqc;
int __clib_unused n_read;
int n_mq_evts, i;
+ double end = -1;
u64 buf;
if (PREDICT_FALSE (wrk->api_client_handle == ~0))
@@ -2628,23 +2702,45 @@ vppcom_select_eventfd (vcl_worker_t * wrk, int n_bits,
}
vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
- n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
- vec_len (wrk->mq_events), time_to_wait);
- for (i = 0; i < n_mq_evts; i++)
+ if (time_to_wait > 0)
+ end = clib_time_now (&wrk->clib_time) + (time_to_wait / 1e3);
+
+ do
{
- if (PREDICT_FALSE (wrk->mq_events[i].data.u32 == ~0))
+ n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
+ vec_len (wrk->mq_events), time_to_wait);
+ if (n_mq_evts < 0)
{
- vcl_api_handle_disconnect (wrk);
- continue;
+ if (errno == EINTR)
+ continue;
+
+ VDBG (0, "epoll_wait error %u", errno);
+ return 0;
}
- mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
- n_read = read (mqc->mq_fd, &buf, sizeof (buf));
- vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
- except_map, 0, bits_set);
+ if (n_mq_evts == 0)
+ return 0;
+
+ for (i = 0; i < n_mq_evts; i++)
+ {
+ if (PREDICT_FALSE (wrk->mq_events[i].data.u32 == ~0))
+ {
+ vcl_api_handle_disconnect (wrk);
+ continue;
+ }
+
+ mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
+ n_read = read (mqc->mq_fd, &buf, sizeof (buf));
+ vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
+ except_map, 0, bits_set);
+ }
+
+ if (*bits_set || !time_to_wait)
+ return (int) *bits_set;
}
+ while (end == -1 || clib_time_now (&wrk->clib_time) < end);
- return (n_mq_evts > 0 ? (int) *bits_set : 0);
+ return 0;
}
int
@@ -3407,6 +3503,9 @@ vppcom_epoll_wait_eventfd (vcl_worker_t *wrk, struct epoll_event *events,
vec_len (wrk->mq_events), timeout_ms);
if (n_mq_evts < 0)
{
+ if (errno == EINTR)
+ continue;
+
VDBG (0, "epoll_wait error %u", errno);
return n_evts;
}
@@ -4613,6 +4712,9 @@ vppcom_proto_str (vppcom_proto_t proto)
case VPPCOM_PROTO_SRTP:
proto_str = "SRTP";
break;
+ case VPPCOM_PROTO_HTTP:
+ proto_str = "HTTP";
+ break;
default:
proto_str = "UNKNOWN";
break;
diff --git a/src/vcl/vppcom.h b/src/vcl/vppcom.h
index 386d7d0c3f7..164dc376ad8 100644
--- a/src/vcl/vppcom.h
+++ b/src/vcl/vppcom.h
@@ -58,6 +58,7 @@ typedef enum vppcom_proto_
VPPCOM_PROTO_QUIC,
VPPCOM_PROTO_DTLS,
VPPCOM_PROTO_SRTP,
+ VPPCOM_PROTO_HTTP,
} vppcom_proto_t;
typedef enum
@@ -259,6 +260,9 @@ extern int vppcom_session_read_segments (uint32_t session_handle,
vppcom_data_segment_t * ds,
uint32_t n_segments,
uint32_t max_bytes);
+extern int vppcom_session_write_segments (uint32_t session_handle,
+ vppcom_data_segment_t * ds,
+ uint32_t n_segments);
extern void vppcom_session_free_segments (uint32_t session_handle,
uint32_t n_bytes);
extern int vppcom_add_cert_key_pair (vppcom_cert_key_pair_t *ckpair);
diff --git a/src/vlib/buffer.c b/src/vlib/buffer.c
index 674f15d5dc6..a7952d93c21 100644
--- a/src/vlib/buffer.c
+++ b/src/vlib/buffer.c
@@ -663,7 +663,8 @@ vlib_buffer_main_init_numa_alloc (struct vlib_main_t *vm, u32 numa_node,
u8 unpriv)
{
vlib_buffer_main_t *bm = vm->buffer_main;
- u32 buffers_per_numa = bm->buffers_per_numa;
+ u32 default_buffers_per_numa = bm->default_buffers_per_numa;
+ u32 buffers_per_numa = bm->buffers_per_numa[numa_node];
clib_error_t *error;
u32 buffer_size;
uword n_pages, pagesize;
@@ -679,9 +680,13 @@ vlib_buffer_main_init_numa_alloc (struct vlib_main_t *vm, u32 numa_node,
return clib_error_return (0, "buffer size (%llu) is greater than page "
"size (%llu)", buffer_size, pagesize);
- if (buffers_per_numa == 0)
- buffers_per_numa = unpriv ? VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA_UNPRIV :
- VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA;
+ if (default_buffers_per_numa == 0)
+ default_buffers_per_numa = unpriv ?
+ VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA_UNPRIV :
+ VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA;
+
+ if (buffers_per_numa == ~0)
+ buffers_per_numa = default_buffers_per_numa;
name = format (0, "buffers-numa-%d%c", numa_node, 0);
n_pages = (buffers_per_numa - 1) / (pagesize / buffer_size) + 1;
@@ -821,7 +826,7 @@ clib_error_t *
vlib_buffer_main_init (struct vlib_main_t * vm)
{
vlib_buffer_main_t *bm;
- clib_error_t *err;
+ clib_error_t *err = 0;
clib_bitmap_t *bmp = 0, *bmp_has_memory = 0;
u32 numa_node;
vlib_buffer_pool_t *bp;
@@ -853,6 +858,10 @@ vlib_buffer_main_init (struct vlib_main_t * vm)
{
u8 *index = bm->default_buffer_pool_index_for_numa + numa_node;
index[0] = ~0;
+
+ if (bm->buffers_per_numa[numa_node] == 0)
+ continue;
+
if ((err = vlib_buffer_main_init_numa_node (vm, numa_node, index)))
{
clib_error_report (err);
@@ -906,18 +915,48 @@ done:
}
static clib_error_t *
+vlib_buffers_numa_configure (vlib_buffer_main_t *bm, u32 numa_node,
+ unformat_input_t *input)
+{
+ u32 buffers = ~0;
+
+ if (numa_node >= VLIB_BUFFER_MAX_NUMA_NODES)
+ return clib_error_return (0, "invalid numa node");
+
+ if (!input)
+ return 0;
+
+ unformat_skip_white_space (input);
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "buffers %u", &buffers))
+ ;
+ else
+ return unformat_parse_error (input);
+ }
+
+ bm->buffers_per_numa[numa_node] = buffers;
+ return 0;
+}
+
+static clib_error_t *
vlib_buffers_configure (vlib_main_t * vm, unformat_input_t * input)
{
vlib_buffer_main_t *bm;
+ u32 numa_node;
+ unformat_input_t sub_input;
+ clib_error_t *error = 0;
vlib_buffer_main_alloc (vm);
bm = vm->buffer_main;
bm->log2_page_size = CLIB_MEM_PAGE_SZ_UNKNOWN;
+ memset (bm->buffers_per_numa, ~0, sizeof (bm->buffers_per_numa));
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
- if (unformat (input, "buffers-per-numa %u", &bm->buffers_per_numa))
+ if (unformat (input, "buffers-per-numa %u",
+ &bm->default_buffers_per_numa))
;
else if (unformat (input, "page-size %U", unformat_log2_page_size,
&bm->log2_page_size))
@@ -925,6 +964,15 @@ vlib_buffers_configure (vlib_main_t * vm, unformat_input_t * input)
else if (unformat (input, "default data-size %u",
&bm->default_data_size))
;
+ else if (unformat (input, "numa %u %U", &numa_node,
+ unformat_vlib_cli_sub_input, &sub_input))
+ {
+ error = vlib_buffers_numa_configure (bm, numa_node, &sub_input);
+ unformat_free (&sub_input);
+
+ if (error)
+ return error;
+ }
else
return unformat_parse_error (input);
}
diff --git a/src/vlib/buffer.h b/src/vlib/buffer.h
index 7d45689ed19..aad9701080e 100644
--- a/src/vlib/buffer.h
+++ b/src/vlib/buffer.h
@@ -508,7 +508,8 @@ typedef struct
u8 default_buffer_pool_index_for_numa[VLIB_BUFFER_MAX_NUMA_NODES];
/* config */
- u32 buffers_per_numa;
+ u32 default_buffers_per_numa;
+ u32 buffers_per_numa[VLIB_BUFFER_MAX_NUMA_NODES];
u16 ext_hdr_size;
u32 default_data_size;
clib_mem_page_sz_t log2_page_size;
diff --git a/src/vlib/cli.c b/src/vlib/cli.c
index 98d57c6ccb0..4198b4b0976 100644
--- a/src/vlib/cli.c
+++ b/src/vlib/cli.c
@@ -43,6 +43,7 @@
#include <vppinfra/callback.h>
#include <vppinfra/cpu.h>
#include <vppinfra/elog.h>
+#include <vppinfra/cJSON.h>
#include <unistd.h>
#include <ctype.h>
@@ -1115,6 +1116,111 @@ VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = {
};
static clib_error_t *
+save_memory_trace (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ char *file, *chroot_file;
+ uword was_enabled;
+ mheap_trace_t *t, *mem_traces = 0;
+ u8 *tmp;
+ cJSON *traces, *trace, *traceback, *symbol;
+ int i;
+ FILE *fp;
+ char *json_str = 0;
+
+ cJSON_Hooks cjson_hooks = {
+ .malloc_fn = clib_mem_alloc,
+ .free_fn = clib_mem_free,
+ .realloc_fn = clib_mem_realloc,
+ };
+ cJSON_InitHooks (&cjson_hooks);
+
+ if (!unformat (input, "%s", &file))
+ {
+ vlib_cli_output (vm, "expected file name, got `%U'",
+ format_unformat_error, input);
+ return 0;
+ }
+
+ /* It's fairly hard to get "../oopsie" through unformat; just in case */
+ if (strstr (file, "..") || strchr (file, '/'))
+ {
+ vlib_cli_output (vm, "illegal characters in filename '%s'", file);
+ return 0;
+ }
+ chroot_file = (char *) format (0, "/tmp/%s%c", file, 0);
+ vec_free (file);
+ fp = fopen ((char *) chroot_file, "w");
+ if (fp == NULL)
+ {
+ vlib_cli_output (vm, "couldn't open output file %s '%s'", chroot_file);
+ vec_free (chroot_file);
+ return 0;
+ }
+
+ was_enabled = clib_mem_trace_enable_disable (0);
+ vlib_cli_output (vm, "Saving trace to '%s'", chroot_file);
+ mem_traces = clib_mem_trace_dup (current_traced_heap);
+ traces = cJSON_CreateArray ();
+ vec_foreach (t, mem_traces)
+ {
+ /* Skip over free elements. */
+ if (t->n_allocations == 0)
+ continue;
+
+ trace = cJSON_CreateObject ();
+ cJSON_AddNumberToObject (trace, "count", t->n_allocations);
+ cJSON_AddNumberToObject (trace, "bytes", t->n_bytes);
+ tmp = format (0, "%p%c", t->offset, 0);
+ cJSON_AddStringToObject (trace, "sample", (char *) tmp);
+ vec_free (tmp);
+ traceback = cJSON_AddArrayToObject (trace, "traceback");
+ for (i = 0; i < ARRAY_LEN (t->callers) && t->callers[i]; i++)
+ {
+#if defined(CLIB_UNIX) && !defined(__APPLE__)
+ tmp = format (0, "%U%c\n", format_clib_elf_symbol_with_address,
+ t->callers[i], 0);
+ symbol = cJSON_CreateString ((char *) tmp);
+ cJSON_AddItemToArray (traceback, symbol);
+ vec_free (tmp);
+#else
+ tmp = format (0, "%p%c\n", t->callers[i], 0);
+ symbol = cJSON_CreateString ((char *) tmp);
+ cJSON_AddItemToArray (traceback, symbol);
+ vec_free (tmp);
+#endif
+ }
+
+ cJSON_AddItemToArray (traces, trace);
+ }
+ json_str = cJSON_PrintUnformatted (traces);
+ cJSON_Delete (traces);
+ fputs (json_str, fp);
+ fclose (fp);
+ clib_mem_free (json_str);
+
+ vec_free (mem_traces);
+ clib_mem_trace_enable_disable (was_enabled);
+
+ vec_free (chroot_file);
+
+ return 0;
+}
+
+/*?
+ * Save memory traces of the currently traced heap in JSON format to file.
+ * Only filename can be specified, path is fixed (/tmp/<filename>).
+ *
+ * @cliexpar
+ * @cliexcmd{save memory-trace mem_trace.json}
+?*/
+VLIB_CLI_COMMAND (save_memory_trace_command, static) = {
+ .path = "save memory-trace",
+ .short_help = "save memory-trace <filename>",
+ .function = save_memory_trace,
+};
+
+static clib_error_t *
restart_cmd_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
{
diff --git a/src/vlib/format.c b/src/vlib/format.c
index 7de6417be69..98010620a5d 100644
--- a/src/vlib/format.c
+++ b/src/vlib/format.c
@@ -198,7 +198,7 @@ unformat_vlib_tmpfile (unformat_input_t * input, va_list * args)
return 0;
/* Brain-police user path input */
- if (strstr ((char *) filename, "..") || index ((char *) filename, '/'))
+ if (strstr ((char *) filename, "..") || strchr ((char *) filename, '/'))
{
vec_free (filename);
return 0;
diff --git a/src/vlib/linux/pci.c b/src/vlib/linux/pci.c
index f7c63bc3607..29ca3d97523 100644
--- a/src/vlib/linux/pci.c
+++ b/src/vlib/linux/pci.c
@@ -1561,14 +1561,17 @@ linux_pci_init (vlib_main_t * vm)
ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32));
- addrs = vlib_pci_get_all_dev_addrs ();
- vec_foreach (addr, addrs)
+ if (pm->pci_device_registrations)
{
- vlib_pci_device_info_t *d;
- if ((d = vlib_pci_get_device_info (vm, addr, 0)))
+ addrs = vlib_pci_get_all_dev_addrs ();
+ vec_foreach (addr, addrs)
{
- init_device_from_registered (vm, d);
- vlib_pci_free_device_info (d);
+ vlib_pci_device_info_t *d;
+ if ((d = vlib_pci_get_device_info (vm, addr, 0)))
+ {
+ init_device_from_registered (vm, d);
+ vlib_pci_free_device_info (d);
+ }
}
}
diff --git a/src/vlib/main.c b/src/vlib/main.c
index 04b58762646..bf840324b64 100644
--- a/src/vlib/main.c
+++ b/src/vlib/main.c
@@ -569,6 +569,9 @@ vlib_node_sync_stats (vlib_main_t * vm, vlib_node_t * n)
uword i;
for (i = 0; i < rt->n_next_nodes; i++)
{
+ if (n->flags & VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES &&
+ n->next_nodes[i] == VLIB_INVALID_NODE_INDEX)
+ continue;
nf = vlib_node_runtime_get_next_frame (vm, rt, i);
vec_elt (n->n_vectors_by_next_node, i) +=
nf->vectors_since_last_overflow;
@@ -655,7 +658,7 @@ elog_save_buffer (vlib_main_t * vm,
}
/* It's fairly hard to get "../oopsie" through unformat; just in case */
- if (strstr (file, "..") || index (file, '/'))
+ if (strstr (file, "..") || strchr (file, '/'))
{
vlib_cli_output (vm, "illegal characters in filename '%s'", file);
return 0;
diff --git a/src/vlib/node.c b/src/vlib/node.c
index 8f6c852188b..c0572f3cf83 100644
--- a/src/vlib/node.c
+++ b/src/vlib/node.c
@@ -805,7 +805,8 @@ vlib_node_main_init (vlib_main_t * vm)
if (!a)
continue;
- if (~0 == vlib_node_add_named_next_with_slot (vm, n->index, a, i))
+ if (~0 == vlib_node_add_named_next_with_slot (vm, n->index, a, i) &&
+ !(n->flags & VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES))
{
error = clib_error_create
("node `%v' refers to unknown node `%s'", n->name, a);
@@ -813,7 +814,8 @@ vlib_node_main_init (vlib_main_t * vm)
}
}
- vec_free (n->next_node_names);
+ if (!(n->flags & VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES))
+ vec_free (n->next_node_names);
}
/* Set previous node pointers. */
@@ -851,14 +853,18 @@ vlib_node_main_init (vlib_main_t * vm)
for (i = 0; i < vec_len (n->next_nodes); i++)
{
- next = vlib_get_node (vm, n->next_nodes[i]);
+ if (n->flags & VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES &&
+ n->next_nodes[i] >= vec_len (nm->nodes))
+ continue;
- /* Validate node runtime indices are correctly initialized. */
- ASSERT (nf[i].node_runtime_index == next->runtime_index);
+ next = vlib_get_node (vm, n->next_nodes[i]);
- nf[i].flags = 0;
- if (next->flags & VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH)
- nf[i].flags |= VLIB_FRAME_NO_FREE_AFTER_DISPATCH;
+ /* Validate node runtime indices are correctly initialized. */
+ ASSERT (nf[i].node_runtime_index == next->runtime_index);
+
+ nf[i].flags = 0;
+ if (next->flags & VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH)
+ nf[i].flags |= VLIB_FRAME_NO_FREE_AFTER_DISPATCH;
}
}
}
@@ -927,6 +933,33 @@ vlib_node_set_march_variant (vlib_main_t *vm, u32 node_index,
}
return -1;
}
+
+clib_error_t *
+vlib_node_main_lazy_next_update (vlib_main_t *vm)
+{
+ vlib_node_main_t *nm = &vm->node_main;
+ uword ni;
+ vlib_node_t *n;
+ for (ni = 0; ni < vec_len (nm->nodes); ni++)
+ {
+ uword nni;
+ n = vec_elt (nm->nodes, ni);
+
+ if (!(n->flags & VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES))
+ continue;
+
+ for (nni = 0; nni < vec_len (n->next_node_names); nni++)
+ {
+ char *a = n->next_node_names[nni];
+
+ if (!a)
+ continue;
+
+ vlib_node_add_named_next_with_slot (vm, n->index, a, nni);
+ }
+ }
+ return 0;
+}
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vlib/node.h b/src/vlib/node.h
index 68813c2c3e1..651a39e3119 100644
--- a/src/vlib/node.h
+++ b/src/vlib/node.h
@@ -297,6 +297,7 @@ typedef struct vlib_node_t
#define VLIB_NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE (1 << 7)
#define VLIB_NODE_FLAG_TRACE_SUPPORTED (1 << 8)
#define VLIB_NODE_FLAG_ADAPTIVE_MODE (1 << 9)
+#define VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES (1 << 10)
/* State for input nodes. */
u8 state;
diff --git a/src/vlib/node_funcs.h b/src/vlib/node_funcs.h
index 1beac33cf9b..4c7020d6a42 100644
--- a/src/vlib/node_funcs.h
+++ b/src/vlib/node_funcs.h
@@ -1338,6 +1338,10 @@ void vlib_node_runtime_sync_stats_node (vlib_node_t *n, vlib_node_runtime_t *r,
/* Node graph initialization function. */
clib_error_t *vlib_node_main_init (vlib_main_t * vm);
+/* Refresh graph after the creation of a node that was potentially mentionned
+ * as a named next for a node with VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES */
+clib_error_t *vlib_node_main_lazy_next_update (vlib_main_t *vm);
+
format_function_t format_vlib_node_graph;
format_function_t format_vlib_node_name;
format_function_t format_vlib_next_node_name;
diff --git a/src/vlib/threads.c b/src/vlib/threads.c
index 87b71adc2bc..ef2c5616f21 100644
--- a/src/vlib/threads.c
+++ b/src/vlib/threads.c
@@ -205,6 +205,10 @@ vlib_thread_init (vlib_main_t * vm)
avail_cpu = clib_bitmap_set (avail_cpu, c, 0);
}
+ /* if main thread affinity is unspecified, set to current running cpu */
+ if (tm->main_lcore == ~0)
+ tm->main_lcore = sched_getcpu ();
+
/* grab cpu for main thread */
if (tm->main_lcore != ~0)
{
@@ -370,6 +374,8 @@ void
vlib_worker_thread_init (vlib_worker_thread_t * w)
{
vlib_thread_main_t *tm = vlib_get_thread_main ();
+ sigset_t signals;
+ int rv;
/*
* Note: disabling signals in worker threads as follows
@@ -379,7 +385,17 @@ vlib_worker_thread_init (vlib_worker_thread_t * w)
* sigfillset (&s);
* pthread_sigmask (SIG_SETMASK, &s, 0);
* }
+ * We can still disable signals for SIGINT,SIGHUP and SIGTERM as they don't
+ * trigger post-dump handlers anyway.
*/
+ sigemptyset (&signals);
+ sigaddset (&signals, SIGINT);
+ sigaddset (&signals, SIGHUP);
+ sigaddset (&signals, SIGTERM);
+ rv = pthread_sigmask (SIG_BLOCK, &signals, NULL);
+
+ if (rv)
+ clib_warning ("Failed to set the worker signal mask");
clib_mem_set_heap (w->thread_mheap);
@@ -1122,6 +1138,7 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input)
u8 *name;
uword *bitmap;
u32 count;
+ int use_corelist = 0;
tm->thread_registrations_by_name = hash_create_string (0, sizeof (uword));
@@ -1173,6 +1190,7 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input)
tr->coremask = bitmap;
tr->count = clib_bitmap_count_set_bits (tr->coremask);
+ use_corelist = 1;
}
else
if (unformat
@@ -1202,6 +1220,9 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input)
break;
}
+ if (use_corelist && tm->main_lcore == ~0)
+ return clib_error_return (0, "main-core must be specified when using "
+ "corelist-* or coremask-* attribute");
if (tm->sched_priority != ~0)
{
if (tm->sched_policy == SCHED_FIFO || tm->sched_policy == SCHED_RR)
diff --git a/src/vlib/threads.h b/src/vlib/threads.h
index ac0c1d5d266..c671aa78c39 100644
--- a/src/vlib/threads.h
+++ b/src/vlib/threads.h
@@ -200,7 +200,7 @@ vlib_smp_unsafe_warning (void)
}
always_inline int
-__foreach_vlib_main_helper (vlib_main_t *ii, vlib_main_t **p)
+__foreach_vlib_main_helper (vlib_main_t *ii, vlib_main_t **p, int checks)
{
vlib_main_t *vm;
u32 index = ii - (vlib_main_t *) 0;
@@ -209,15 +209,17 @@ __foreach_vlib_main_helper (vlib_main_t *ii, vlib_main_t **p)
return 0;
*p = vm = vlib_global_main.vlib_mains[index];
- ASSERT (index == 0 || vm->parked_at_barrier == 1);
+ ASSERT (!checks || index == 0 || vm->parked_at_barrier == 1);
return 1;
}
-#define foreach_vlib_main() \
+#define foreach_vlib_main__(checks) \
for (vlib_main_t *ii = 0, *this_vlib_main; \
- __foreach_vlib_main_helper (ii, &this_vlib_main); ii++) \
+ __foreach_vlib_main_helper (ii, &this_vlib_main, checks); ii++) \
if (this_vlib_main)
+#define foreach_vlib_main() foreach_vlib_main__ (1)
+
#define foreach_sched_policy_posix \
_ (SCHED_OTHER, OTHER, "other") \
_ (SCHED_FIFO, FIFO, "fifo") \
diff --git a/src/vlib/unix/cli.c b/src/vlib/unix/cli.c
index 90cf61d811d..051c5730aed 100644
--- a/src/vlib/unix/cli.c
+++ b/src/vlib/unix/cli.c
@@ -3329,6 +3329,12 @@ VLIB_CLI_COMMAND (unix_cli_q_command, static) = {
.function = unix_cli_quit,
};
+VLIB_CLI_COMMAND (unix_cli_exit_command, static) = {
+ .path = "exit",
+ .short_help = "Exit CLI",
+ .function = unix_cli_quit,
+};
+
/** CLI command to execute a VPP command script. */
static clib_error_t *
unix_cli_exec (vlib_main_t * vm,
diff --git a/src/vlib/unix/main.c b/src/vlib/unix/main.c
index ee28ca8f1aa..11d0cb1160c 100644
--- a/src/vlib/unix/main.c
+++ b/src/vlib/unix/main.c
@@ -40,6 +40,8 @@
#include <vlib/unix/unix.h>
#include <vlib/unix/plugin.h>
#include <vppinfra/unix.h>
+#include <vppinfra/stack.h>
+#include <vppinfra/format_ansi.h>
#include <limits.h>
#include <signal.h>
@@ -97,20 +99,42 @@ int vlib_last_signum = 0;
uword vlib_last_faulting_address = 0;
static void
+log_one_line ()
+{
+ vec_terminate_c_string (syslog_msg);
+ if (unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG))
+ fprintf (stderr, "%s\n", syslog_msg);
+ else
+ syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
+ vec_reset_length (syslog_msg);
+}
+
+static void
unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
{
uword fatal = 0;
+ int color =
+ (unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG)) &&
+ (unix_main.flags & UNIX_FLAG_NOCOLOR) == 0;
/* These come in handy when looking at core files from optimized images */
vlib_last_signum = signum;
vlib_last_faulting_address = (uword) si->si_addr;
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_BR_RED);
+
syslog_msg = format (syslog_msg, "received signal %U, PC %U",
format_signal, signum, format_ucontext_pc, uc);
- if (signum == SIGSEGV)
+ if (signum == SIGSEGV || signum == SIGBUS)
syslog_msg = format (syslog_msg, ", faulting address %p", si->si_addr);
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
+
+ log_one_line ();
+
switch (signum)
{
/* these (caught) signals cause the application to exit */
@@ -120,11 +144,17 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
*/
if (unix_main.vlib_main && unix_main.vlib_main->main_loop_exit_set)
{
- syslog (LOG_ERR | LOG_DAEMON, "received SIGTERM, exiting...");
+ syslog_msg = format (
+ syslog_msg, "received SIGTERM from PID %d UID %d, exiting...",
+ si->si_pid, si->si_uid);
+ log_one_line ();
unix_main.vlib_main->main_loop_exit_now = 1;
}
else
- syslog (LOG_ERR | LOG_DAEMON, "IGNORE early SIGTERM...");
+ {
+ syslog_msg = format (syslog_msg, "IGNORE early SIGTERM...");
+ log_one_line ();
+ }
break;
/* fall through */
case SIGQUIT:
@@ -144,26 +174,75 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
break;
}
- /* Null terminate. */
- vec_add1 (syslog_msg, 0);
if (fatal)
{
- syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
+ int skip = 1, index = 0;
- /* Address of callers: outer first, inner last. */
- uword callers[15];
- uword n_callers = clib_backtrace (callers, ARRAY_LEN (callers), 0);
- int i;
- for (i = 0; i < n_callers; i++)
+ foreach_clib_stack_frame (sf)
{
- vec_reset_length (syslog_msg);
+ if (sf->is_signal_frame)
+ {
+ int pipefd[2];
+ const int n_bytes = 20;
+ u8 *ip = (void *) sf->ip;
+
+ if (pipe (pipefd) == 0)
+ {
+ /* check PC points to valid memory */
+ if (write (pipefd[1], ip, n_bytes) == n_bytes)
+ {
+ syslog_msg = format (syslog_msg, "Code: ");
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_CYAN);
+ for (int i = 0; i < n_bytes; i++)
+ syslog_msg = format (syslog_msg, " %02x", ip[i]);
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
+ }
+ else
+ {
+ syslog_msg = format (
+ syslog_msg, "PC contains invalid memory address");
+ }
+ log_one_line ();
+ foreach_int (i, 0, 1)
+ close (pipefd[i]);
+ }
+ skip = 0;
+ }
+
+ if (skip)
+ continue;
+
+ syslog_msg = format (syslog_msg, "#%-2d ", index++);
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_BLUE);
+ syslog_msg = format (syslog_msg, "0x%016lx", sf->ip);
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
+
+ if (sf->name[0])
+ {
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_YELLOW);
+ syslog_msg =
+ format (syslog_msg, " %s + 0x%x", sf->name, sf->offset);
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
+ }
- syslog_msg =
- format (syslog_msg, "#%-2d 0x%016lx %U%c", i, callers[i],
- format_clib_elf_symbol_with_address, callers[i], 0);
+ log_one_line ();
- syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
+ if (sf->file_name)
+ {
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_GREEN);
+ syslog_msg = format (syslog_msg, " from %s", sf->file_name);
+ if (color)
+ syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
+ log_one_line ();
+ }
}
/* have to remove SIGABRT to avoid recursive - os_exit calling abort() */
@@ -175,9 +254,6 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
else
os_exit (1);
}
- else
- clib_warning ("%s", syslog_msg);
-
}
static clib_error_t *
diff --git a/src/vlib/unix/plugin.c b/src/vlib/unix/plugin.c
index 5cac9abc8fe..77e4633e14a 100644
--- a/src/vlib/unix/plugin.c
+++ b/src/vlib/unix/plugin.c
@@ -88,19 +88,14 @@ extract (u8 * sp, u8 * ep)
*/
static clib_error_t *
-r2_to_reg (elf_main_t * em, vlib_plugin_r2_t * r2,
- vlib_plugin_registration_t * reg)
+r2_to_reg (elf_main_t *em, vlib_plugin_r2_t *r2,
+ vlib_plugin_registration_t *reg, elf_section_t *data_section)
{
- clib_error_t *error;
- elf_section_t *section;
uword data_segment_offset;
u8 *data;
/* It turns out that the strings land in the ".data" section */
- error = elf_get_section_by_name (em, ".data", &section);
- if (error)
- return error;
- data = elf_get_section_contents (em, section->index, 1);
+ data = elf_get_section_contents (em, data_section->index, 1);
/*
* Offsets in the ".vlib_plugin_r2" section
@@ -177,13 +172,52 @@ load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init)
error = elf_get_section_by_name (&em, ".vlib_plugin_r2", &section);
if (error == 0)
{
+ elf_section_t *data_section;
+ elf_relocation_table_t *rt;
+ elf_relocation_with_addend_t *r;
+ elf_symbol_table_t *st;
+ elf64_symbol_t *sym, *symok = 0;
+
data = elf_get_section_contents (&em, section->index, 1);
r2 = (vlib_plugin_r2_t *) data;
+
+ elf_get_section_by_name (&em, ".data", &data_section);
+
+ // Find first symbol in .vlib_plugin_r2 section.
+ vec_foreach (st, em.symbol_tables)
+ {
+ vec_foreach (sym, st->symbols)
+ {
+ if (sym->section_index == section->index)
+ {
+ symok = sym;
+ break;
+ }
+ }
+ }
+
+ // Relocate section data as per relocation tables.
+ if (symok != 0)
+ {
+ vec_foreach (rt, em.relocation_tables)
+ {
+ vec_foreach (r, rt->relocations)
+ {
+ if (r->address >= symok->value &&
+ r->address < symok->value + symok->size)
+ {
+ *(uword *) ((void *) data + r->address - symok->value) +=
+ r->addend - data_section->header.exec_address;
+ }
+ }
+ }
+ }
+
reg = clib_mem_alloc (sizeof (*reg));
memset (reg, 0, sizeof (*reg));
reg->default_disabled = r2->default_disabled != 0;
- error = r2_to_reg (&em, r2, reg);
+ error = r2_to_reg (&em, r2, reg, data_section);
if (error)
{
PLUGIN_LOG_ERR ("Bad r2 registration: %s\n", (char *) pi->name);
diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c
index 79064b292c9..1f02aefe88a 100644
--- a/src/vlibapi/api_shared.c
+++ b/src/vlibapi/api_shared.c
@@ -230,7 +230,7 @@ vl_msg_api_trace_write_one (api_main_t *am, u8 *msg, FILE *fp)
if (m && m->endian_handler)
{
- m->endian_handler (tmpmem, 1);
+ m->endian_handler (tmpmem, 0 /* host endian */);
}
if (m && m->tojson_handler)
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index fb8d294009d..6262e803057 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -28,6 +28,7 @@ list(APPEND VNET_SOURCES
devices/netlink.c
dev/api.c
dev/args.c
+ dev/bus/pci.c
dev/cli.c
dev/config.c
dev/counters.c
@@ -36,7 +37,6 @@ list(APPEND VNET_SOURCES
dev/error.c
dev/format.c
dev/handlers.c
- dev/pci.c
dev/port.c
dev/process.c
dev/queue.c
@@ -74,7 +74,17 @@ list(APPEND VNET_HEADERS
config.h
devices/devices.h
devices/netlink.h
+ dev/api.h
+ dev/args.h
+ dev/bus/pci.h
+ dev/counters.h
+ dev/dev_funcs.h
dev/dev.h
+ dev/errors.h
+ dev/log.h
+ dev/mgmt.h
+ dev/process.h
+ dev/types.h
flow/flow.h
global_funcs.h
interface/rx_queue_funcs.h
@@ -94,6 +104,7 @@ list(APPEND VNET_HEADERS
format_fns.h
ip/ip_format_fns.h
ip/ip_sas.h
+ ip/vtep.h
ethernet/ethernet_format_fns.h
ethernet/ethernet_types_api.h
)
@@ -596,19 +607,6 @@ list(APPEND VNET_HEADERS
)
##############################################################################
-# Layer 3 protocol: osi
-##############################################################################
-list(APPEND VNET_SOURCES
- osi/node.c
- osi/osi.c
- osi/pg.c
-)
-
-list(APPEND VNET_HEADERS
- osi/osi.h
-)
-
-##############################################################################
# Layer 4 protocol: tcp
##############################################################################
list(APPEND VNET_SOURCES
@@ -813,24 +811,6 @@ list(APPEND VNET_API_FILES
)
##############################################################################
-# mpls segment routing
-##############################################################################
-
-list(APPEND VNET_SOURCES
- srmpls/sr_mpls_policy.c
- srmpls/sr_mpls_steering.c
- srmpls/sr_mpls_api.c
-)
-
-list(APPEND VNET_HEADERS
- srmpls/sr_mpls.h
-)
-
-list(APPEND VNET_API_FILES
- srmpls/sr_mpls.api
-)
-
-##############################################################################
# IPFIX / netflow v10
##############################################################################
list(APPEND VNET_SOURCES
@@ -980,12 +960,14 @@ list(APPEND VNET_API_FILES
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
list(APPEND VNET_SOURCES
+ dev/bus/platform.c
devices/tap/cli.c
devices/tap/tap.c
devices/tap/tapv2_api.c
)
list(APPEND VNET_HEADERS
+ dev/bus/platform.h
devices/tap/tap.h
)
@@ -1030,6 +1012,7 @@ list(APPEND VNET_SOURCES
session/application_namespace.c
session/segment_manager.c
session/session_api.c
+ session/session_sdl.c
)
list(APPEND VNET_HEADERS
@@ -1060,10 +1043,13 @@ list(APPEND VNET_API_FILES session/session.api)
list(APPEND VNET_SOURCES
tls/tls.c
+ tls/tls_record.c
)
list(APPEND VNET_HEADERS
tls/tls.h
+ tls/tls_inlines.h
+ tls/tls_record.h
tls/tls_test.h
)
@@ -1162,6 +1148,7 @@ list(APPEND VNET_HEADERS
fib/fib_path_list.h
fib/fib_sas.h
fib/fib_source.h
+ fib/fib_urpf_list.h
)
list(APPEND VNET_API_FILES
@@ -1409,6 +1396,7 @@ list (APPEND VNET_SOURCES
list(APPEND VNET_HEADERS
arp/arp.h
+ arp/arp_packet.h
)
list(APPEND VNET_API_FILES arp/arp.api)
@@ -1460,7 +1448,6 @@ add_vat_test_library(vnet
ip/ip_test.c
arp/arp_test.c
ip6-nd/ip6_nd_test.c
- srmpls/sr_mpls_test.c
session/session_test.c
l2/l2_test.c
ipsec/ipsec_test.c
diff --git a/src/vnet/dev/pci.c b/src/vnet/dev/bus/pci.c
index 3cc0cba5003..4bb8660f4b4 100644
--- a/src/vnet/dev/pci.c
+++ b/src/vnet/dev/bus/pci.c
@@ -4,7 +4,7 @@
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
-#include <vnet/dev/pci.h>
+#include <vnet/dev/bus/pci.h>
#include <vnet/dev/log.h>
#include <vlib/unix/unix.h>
diff --git a/src/vnet/dev/pci.h b/src/vnet/dev/bus/pci.h
index ce9a53aa273..ce9a53aa273 100644
--- a/src/vnet/dev/pci.h
+++ b/src/vnet/dev/bus/pci.h
diff --git a/src/vnet/dev/bus/platform.c b/src/vnet/dev/bus/platform.c
new file mode 100644
index 00000000000..56a1f9c762c
--- /dev/null
+++ b/src/vnet/dev/bus/platform.c
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/bus/platform.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
+ .class_name = "dev",
+ .subclass_name = "platform",
+};
+
+#define log_debug(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_DEBUG, dev_log.class, "%U" f, format_vnet_dev_log, \
+ dev, \
+ clib_string_skip_prefix (__func__, "vnet_dev_bus_platform_dt_"), \
+ ##__VA_ARGS__)
+#define log_err(dev, f, ...) \
+ vlib_log (VLIB_LOG_LEVEL_ERR, dev_log.class, "%U" f, format_vnet_dev_log, \
+ dev, 0, ##__VA_ARGS__)
+
+#define PLATFORM_DEV_PATH "/sys/bus/platform/devices"
+
+clib_dt_main_t vnet_dev_bus_platform_dt_main;
+
+vnet_dev_rv_t
+vnet_dev_bus_platform_dt_node_from_device_id (clib_dt_node_t **nodep,
+ char *device_id)
+{
+ clib_dt_main_t *dm = &vnet_dev_bus_platform_dt_main;
+ clib_dt_node_t *n;
+ char *name = device_id + sizeof (PLATFORM_BUS_NAME);
+ char path[PATH_MAX];
+ int r;
+ u8 *link;
+
+ if (dm->root == 0)
+ {
+ clib_error_t *err;
+ err = clib_dt_read_from_sysfs (&vnet_dev_bus_platform_dt_main);
+ if (err)
+ {
+ log_err (0, "cannot read devicetree: %U", format_clib_error, err);
+ clib_error_free (err);
+ return VNET_DEV_ERR_NOT_FOUND;
+ }
+ }
+
+ link = format (0, PLATFORM_DEV_PATH "/%s/of_node%c", name, 0);
+ r = readlink ((char *) link, path, sizeof (path) - 1);
+
+ if (r < 1)
+ {
+ log_err (0, "of_node doesn't exist for '%s'", name);
+ vec_free (link);
+ return VNET_DEV_ERR_NOT_FOUND;
+ }
+
+ path[r] = 0;
+ vec_reset_length (link);
+ link = format (link, PLATFORM_DEV_PATH "/%s/%s%c", name, path, 0);
+ if (!realpath ((char *) link, path))
+ {
+ log_err (0, "cannot find realpath for '%s'", link);
+ vec_free (link);
+ return VNET_DEV_ERR_NOT_FOUND;
+ }
+
+ vec_free (link);
+
+ if (strncmp (CLIB_DT_LINUX_PREFIX, path,
+ sizeof (CLIB_DT_LINUX_PREFIX) - 1) != 0)
+ return VNET_DEV_ERR_BUG;
+
+ n = clib_dt_get_node_with_path (dm, "%s",
+ path + sizeof (CLIB_DT_LINUX_PREFIX) - 1);
+
+ if (n)
+ {
+ *nodep = n;
+ return VNET_DEV_OK;
+ }
+
+ return VNET_DEV_ERR_NOT_FOUND;
+}
+
+static void *
+vnet_dev_bus_platform_get_device_info (vlib_main_t *vm, char *device_id)
+{
+ clib_dt_node_t *n = 0;
+ vnet_dev_bus_platform_device_info_t *di;
+
+ vnet_dev_bus_platform_dt_node_from_device_id (&n, device_id);
+
+ if (n)
+ {
+ clib_dt_property_t *compatible;
+ compatible = clib_dt_get_node_property_by_name (n, "compatible");
+ log_debug (0, "node found, is compatible %U",
+ format_clib_dt_property_data, compatible);
+ di = clib_mem_alloc (sizeof (*di));
+ di->node = n;
+ return di;
+ }
+
+ return 0;
+}
+
+static void
+vnet_dev_bus_platform_free_device_info (vlib_main_t *vm, void *p)
+{
+ clib_mem_free (p);
+}
+
+static void
+vnet_dev_bus_platform_close (vlib_main_t *vm, vnet_dev_t *dev)
+{
+ log_debug (dev, "");
+}
+
+static vnet_dev_rv_t
+vnet_dev_bus_platform_open (vlib_main_t *vm, vnet_dev_t *dev)
+{
+ clib_dt_node_t *n = 0;
+ vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev);
+ vnet_dev_rv_t rv;
+
+ log_debug (dev, "");
+
+ rv = vnet_dev_bus_platform_dt_node_from_device_id (&n, dev->device_id);
+ if (rv != VNET_DEV_OK)
+ return rv;
+
+ dd->node = n;
+ return VNET_DEV_OK;
+}
+
+static u8 *
+format_dev_bus_platform_device_info (u8 *s, va_list *args)
+{
+ vnet_dev_format_args_t __clib_unused *a =
+ va_arg (*args, vnet_dev_format_args_t *);
+ vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
+ vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev);
+ return format (s, "device-tree path is '%v'", dd->node->path);
+}
+
+static u8 *
+format_dev_bus_platform_device_addr (u8 *s, va_list *args)
+{
+ vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
+ return format (s, "%s", dev->device_id + sizeof (PLATFORM_BUS_NAME));
+}
+
+VNET_DEV_REGISTER_BUS (pp2) = {
+ .name = PLATFORM_BUS_NAME,
+ .device_data_size = sizeof (vnet_dev_bus_platform_device_info_t),
+ .ops = {
+ .get_device_info = vnet_dev_bus_platform_get_device_info,
+ .free_device_info = vnet_dev_bus_platform_free_device_info,
+ .device_open = vnet_dev_bus_platform_open,
+ .device_close = vnet_dev_bus_platform_close,
+ .format_device_info = format_dev_bus_platform_device_info,
+ .format_device_addr = format_dev_bus_platform_device_addr,
+ },
+};
diff --git a/src/vnet/dev/bus/platform.h b/src/vnet/dev/bus/platform.h
new file mode 100644
index 00000000000..3492aad57ed
--- /dev/null
+++ b/src/vnet/dev/bus/platform.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#ifndef _VNET_DEV_BUS_PLATFORM_H_
+#define _VNET_DEV_BUS_PLATFORM_H_
+
+#include <vppinfra/clib.h>
+#include <vppinfra/devicetree.h>
+#include <vlib/vlib.h>
+#include <vnet/dev/dev.h>
+
+#define PLATFORM_BUS_NAME "platform"
+
+extern clib_dt_main_t vnet_dev_bus_platform_dt_main;
+
+typedef struct
+{
+ clib_dt_node_t *node;
+} vnet_dev_bus_platform_device_info_t;
+
+typedef struct
+{
+ clib_dt_node_t *node;
+} vnet_dev_bus_platform_device_data_t;
+
+#endif /* _VNET_DEV_BUS_PLATFORM_H_ */
diff --git a/src/vnet/dev/counters.c b/src/vnet/dev/counters.c
index 0a1e0a7419d..d02839d664f 100644
--- a/src/vnet/dev/counters.c
+++ b/src/vnet/dev/counters.c
@@ -54,7 +54,7 @@ vnet_dev_counters_clear (vlib_main_t *vm, vnet_dev_counter_main_t *cm)
{
for (int i = 0; i < cm->n_counters; i++)
{
- cm->counter_start[i] = cm->counter_data[i];
+ cm->counter_start[i] += cm->counter_data[i];
cm->counter_data[i] = 0;
}
}
diff --git a/src/vnet/dev/dev.h b/src/vnet/dev/dev.h
index bbf2f9dff21..eb06eeba34e 100644
--- a/src/vnet/dev/dev.h
+++ b/src/vnet/dev/dev.h
@@ -115,6 +115,7 @@ typedef struct
vnet_dev_rx_queue_op_t *start;
vnet_dev_rx_queue_op_no_rv_t *stop;
vnet_dev_rx_queue_op_no_rv_t *free;
+ vnet_dev_rx_queue_op_no_rv_t *clear_counters;
format_function_t *format_info;
} vnet_dev_rx_queue_ops_t;
@@ -124,6 +125,7 @@ typedef struct
vnet_dev_tx_queue_op_t *start;
vnet_dev_tx_queue_op_no_rv_t *stop;
vnet_dev_tx_queue_op_no_rv_t *free;
+ vnet_dev_tx_queue_op_no_rv_t *clear_counters;
format_function_t *format_info;
} vnet_dev_tx_queue_ops_t;
@@ -245,6 +247,7 @@ typedef struct
vnet_dev_port_op_no_rv_t *stop;
vnet_dev_port_op_no_rv_t *deinit;
vnet_dev_port_op_no_rv_t *free;
+ vnet_dev_port_op_no_rv_t *clear_counters;
format_function_t *format_status;
format_function_t *format_flow;
} vnet_dev_port_ops_t;
diff --git a/src/vnet/dev/errors.h b/src/vnet/dev/errors.h
index 430a6aef282..6ececad12ec 100644
--- a/src/vnet/dev/errors.h
+++ b/src/vnet/dev/errors.h
@@ -41,6 +41,7 @@
_ (UNSUPPORTED_DEVICE, "unsupported device") \
_ (UNSUPPORTED_DEVICE_VER, "unsupported device version") \
_ (ALREADY_DONE, "already done") \
- _ (NO_SUCH_INTERFACE, "no such interface")
+ _ (NO_SUCH_INTERFACE, "no such interface") \
+ _ (INIT_FAILED, "init failed")
#endif /* _VNET_DEV_ERRORS_H_ */
diff --git a/src/vnet/dev/format.c b/src/vnet/dev/format.c
index ed83a0eba95..f599c0f8b85 100644
--- a/src/vnet/dev/format.c
+++ b/src/vnet/dev/format.c
@@ -101,7 +101,7 @@ format_vnet_dev_port_info (u8 *s, va_list *args)
u32 indent = format_get_indent (s);
s = format (s, "Hardware Address is %U", format_vnet_dev_hw_addr,
- &port->attr.hw_addr);
+ &port->primary_hw_addr);
s = format (s, ", %u RX queues (max %u), %u TX queues (max %u)",
pool_elts (port->rx_queues), port->attr.max_rx_queues,
pool_elts (port->tx_queues), port->attr.max_tx_queues);
diff --git a/src/vnet/dev/port.c b/src/vnet/dev/port.c
index 8a6df54cbc8..df7805c1ff2 100644
--- a/src/vnet/dev/port.c
+++ b/src/vnet/dev/port.c
@@ -305,7 +305,8 @@ vnet_dev_port_cfg_change_req_validate (vlib_main_t *vm, vnet_dev_port_t *port,
switch (req->type)
{
case VNET_DEV_PORT_CFG_MAX_RX_FRAME_SIZE:
- if (req->max_rx_frame_size > port->attr.max_supported_rx_frame_size)
+ if ((req->max_rx_frame_size > port->attr.max_supported_rx_frame_size) ||
+ (req->max_rx_frame_size < ETHERNET_MIN_PACKET_BYTES))
return VNET_DEV_ERR_INVALID_VALUE;
if (req->max_rx_frame_size == port->max_rx_frame_size)
return VNET_DEV_ERR_NO_CHANGE;
@@ -733,16 +734,26 @@ vnet_dev_port_if_remove (vlib_main_t *vm, vnet_dev_port_t *port)
void
vnet_dev_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port)
{
- if (port->counter_main)
+ if (port->port_ops.clear_counters)
+ port->port_ops.clear_counters (vm, port);
+ else if (port->counter_main)
vnet_dev_counters_clear (vm, port->counter_main);
foreach_vnet_dev_port_rx_queue (q, port)
- if (q->counter_main)
- vnet_dev_counters_clear (vm, q->counter_main);
+ {
+ if (port->rx_queue_ops.clear_counters)
+ port->rx_queue_ops.clear_counters (vm, q);
+ else if (q->counter_main)
+ vnet_dev_counters_clear (vm, q->counter_main);
+ }
foreach_vnet_dev_port_tx_queue (q, port)
- if (q->counter_main)
- vnet_dev_counters_clear (vm, q->counter_main);
+ {
+ if (port->tx_queue_ops.clear_counters)
+ port->tx_queue_ops.clear_counters (vm, q);
+ else if (q->counter_main)
+ vnet_dev_counters_clear (vm, q->counter_main);
+ }
log_notice (port->dev, "counters cleared on port %u", port->port_id);
}
diff --git a/src/vnet/devices/tap/tap.c b/src/vnet/devices/tap/tap.c
index 1e2ee87041d..b0b0a3af13f 100644
--- a/src/vnet/devices/tap/tap.c
+++ b/src/vnet/devices/tap/tap.c
@@ -85,7 +85,7 @@ virtio_eth_set_max_frame_size (vnet_main_t *vnm, vnet_hw_interface_t *hi,
return 0;
}
-#define TAP_MAX_INSTANCE 1024
+#define TAP_MAX_INSTANCE 8192
static void
tap_free (vlib_main_t * vm, virtio_if_t * vif)
@@ -162,7 +162,8 @@ tap_create_if (vlib_main_t * vm, tap_create_if_args_t * args)
}
else
{
- args->id = clib_bitmap_first_clear (tm->tap_ids);
+ args->id = clib_bitmap_next_clear (tm->tap_ids, args->auto_id_offset %
+ TAP_MAX_INSTANCE);
}
if (args->id > TAP_MAX_INSTANCE)
diff --git a/src/vnet/devices/tap/tap.h b/src/vnet/devices/tap/tap.h
index 6b88c34fe41..1df2fb7e1ad 100644
--- a/src/vnet/devices/tap/tap.h
+++ b/src/vnet/devices/tap/tap.h
@@ -42,6 +42,7 @@ typedef enum
typedef struct
{
u32 id;
+ u32 auto_id_offset;
u8 mac_addr_set;
mac_address_t mac_addr;
u16 num_rx_queues;
diff --git a/src/vnet/devices/virtio/FEATURE.yaml b/src/vnet/devices/virtio/FEATURE.yaml
index 446a45b61a3..870023861de 100644
--- a/src/vnet/devices/virtio/FEATURE.yaml
+++ b/src/vnet/devices/virtio/FEATURE.yaml
@@ -13,6 +13,7 @@ features:
- Support virtio 1.1 packed ring in vhost
- Support for tx queue size configuration (tested on host kernel 5.15
and qemu version 6.2.0)
+ - Support RSS (tested on ubuntu 23.10)
description: "Virtio implementation"
missing:
- API dump filtering by sw_if_index
diff --git a/src/vnet/devices/virtio/cli.c b/src/vnet/devices/virtio/cli.c
index c1b6c8be065..c4364600722 100644
--- a/src/vnet/devices/virtio/cli.c
+++ b/src/vnet/devices/virtio/cli.c
@@ -62,6 +62,8 @@ virtio_pci_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
args.bind = VIRTIO_BIND_FORCE;
else if (unformat (line_input, "bind"))
args.bind = VIRTIO_BIND_DEFAULT;
+ else if (unformat (line_input, "rss-enabled"))
+ args.rss_enabled = 1;
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
@@ -77,7 +79,7 @@ VLIB_CLI_COMMAND (virtio_pci_create_command, static) = {
.path = "create interface virtio",
.short_help = "create interface virtio <pci-address> "
"[feature-mask <hex-mask>] [tx-queue-size <size>] "
- "[gso-enabled] [csum-enabled] "
+ "[gso-enabled] [csum-enabled] [rss-enabled] "
"[buffering [size <buffering-szie>]] [packed] [bind [force]]",
.function = virtio_pci_create_command_fn,
};
diff --git a/src/vnet/devices/virtio/node.c b/src/vnet/devices/virtio/node.c
index 8c837575cf8..027e1ed4e74 100644
--- a/src/vnet/devices/virtio/node.c
+++ b/src/vnet/devices/virtio/node.c
@@ -282,6 +282,16 @@ virtio_device_input_gso_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
if (n_left == 0)
return 0;
+ if (PREDICT_FALSE (n_left == vring->queue_size))
+ {
+ /*
+ * Informational error logging when VPP is not pulling packets fast
+ * enough.
+ */
+ vlib_error_count (vm, node->node_index, VIRTIO_INPUT_ERROR_FULL_RX_QUEUE,
+ 1);
+ }
+
if (type == VIRTIO_IF_TYPE_TUN)
{
next_index = VNET_DEVICE_INPUT_NEXT_IP4_INPUT;
diff --git a/src/vnet/devices/virtio/pci.c b/src/vnet/devices/virtio/pci.c
index 6234f64fcfb..140cdb94153 100644
--- a/src/vnet/devices/virtio/pci.c
+++ b/src/vnet/devices/virtio/pci.c
@@ -37,6 +37,13 @@
#define PCI_MSIX_ENABLE 0x8000
+static const u8 virtio_rss_key[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67,
+ 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb,
+ 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30,
+ 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+};
+
static pci_device_id_t virtio_pci_device_ids[] = {
{
.vendor_id = PCI_VENDOR_ID_VIRTIO,
@@ -584,6 +591,35 @@ virtio_pci_enable_multiqueue (vlib_main_t * vm, virtio_if_t * vif,
return status;
}
+static int
+virtio_pci_enable_multiqueue_rss (vlib_main_t *vm, virtio_if_t *vif,
+ u16 num_queues)
+{
+ virtio_ctrl_msg_t mq_hdr;
+ virtio_net_rss_config *rss = (virtio_net_rss_config *) mq_hdr.data;
+ virtio_net_ctrl_ack_t status = VIRTIO_NET_ERR;
+
+ STATIC_ASSERT (sizeof (*rss) <= sizeof (mq_hdr.data),
+ "virtio_net_rss_config size too big");
+ mq_hdr.ctrl.class = VIRTIO_NET_CTRL_MQ;
+ mq_hdr.ctrl.cmd = VIRTIO_NET_CTRL_MQ_RSS_CONFIG;
+ mq_hdr.status = VIRTIO_NET_ERR;
+
+ rss->hash_types = VIRTIO_NET_HASH_TYPE_SUPPORTED;
+ rss->indirection_table_mask = VIRTIO_NET_RSS_MAX_TABLE_LEN - 1;
+ rss->unclassified_queue = 0;
+ for (int i = 0; i < VIRTIO_NET_RSS_MAX_TABLE_LEN; i++)
+ rss->indirection_table[i] = i % num_queues;
+ rss->max_tx_vq = num_queues;
+ rss->hash_key_length = VIRTIO_NET_RSS_MAX_KEY_SIZE;
+ clib_memcpy (rss->hash_key_data, virtio_rss_key,
+ VIRTIO_NET_RSS_MAX_KEY_SIZE);
+
+ status = virtio_pci_send_ctrl_msg (vm, vif, &mq_hdr, sizeof (*rss));
+ virtio_log_debug (vif, "multi-queue with rss enable %u queues", num_queues);
+ return status;
+}
+
static u8
virtio_pci_queue_size_valid (u16 qsz)
{
@@ -933,6 +969,9 @@ virtio_negotiate_features (vlib_main_t * vm, virtio_if_t * vif,
| VIRTIO_FEATURE (VIRTIO_F_ANY_LAYOUT)
| VIRTIO_FEATURE (VIRTIO_RING_F_INDIRECT_DESC);
+ if (vif->rss_enabled)
+ supported_features |= VIRTIO_FEATURE (VIRTIO_NET_F_RSS);
+
if (vif->is_modern)
supported_features |= VIRTIO_FEATURE (VIRTIO_F_VERSION_1);
@@ -1375,6 +1414,7 @@ virtio_pci_create_if (vlib_main_t * vm, virtio_pci_create_if_args_t * args)
vif->dev_instance = vif - vim->interfaces;
vif->per_interface_next_index = ~0;
vif->pci_addr.as_u32 = args->addr;
+ vif->rss_enabled = args->rss_enabled;
if (args->virtio_flags & VIRTIO_FLAG_PACKED)
vif->is_packed = 1;
@@ -1536,8 +1576,16 @@ virtio_pci_create_if (vlib_main_t * vm, virtio_pci_create_if_args_t * args)
if ((vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_CTRL_VQ)) &&
(vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_MQ)))
{
- if (virtio_pci_enable_multiqueue (vm, vif, vif->max_queue_pairs))
- virtio_log_warning (vif, "multiqueue is not set");
+ if (vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_RSS))
+ {
+ if (virtio_pci_enable_multiqueue_rss (vm, vif, vif->max_queue_pairs))
+ virtio_log_warning (vif, "multiqueue with rss is not set");
+ }
+ else
+ {
+ if (virtio_pci_enable_multiqueue (vm, vif, vif->max_queue_pairs))
+ virtio_log_warning (vif, "multiqueue is not set");
+ }
}
return;
diff --git a/src/vnet/devices/virtio/pci.h b/src/vnet/devices/virtio/pci.h
index 5eb80f823be..745ad6fce87 100644
--- a/src/vnet/devices/virtio/pci.h
+++ b/src/vnet/devices/virtio/pci.h
@@ -60,6 +60,9 @@ typedef enum
/* If multiqueue is provided by host, then we support it. */
#define VIRTIO_NET_CTRL_MQ 4
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
+#define VIRTIO_NET_CTRL_MQ_RSS_CONFIG 1
+#define VIRTIO_NET_CTRL_MQ_HASH_CONFIG 2
+
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
@@ -139,14 +142,77 @@ typedef struct
u64 queue_device; /* read-write */
} virtio_pci_common_cfg_t;
+#define foreach_virtio_net_hash_report_type \
+ _ (NONE, 0) \
+ _ (IPV4, 1) \
+ _ (TCPV4, 2) \
+ _ (UDPV4, 3) \
+ _ (IPV6, 4) \
+ _ (TCPV6, 5) \
+ _ (UDPV6, 6) \
+ _ (IPV6_EX, 7) \
+ _ (TCPV6_EX, 8) \
+ _ (UDPV6_EX, 9)
+
+typedef enum
+{
+#define _(n, i) VIRTIO_NET_HASH_REPORT_##n = i,
+ foreach_virtio_net_hash_report_type
+#undef _
+} virtio_net_hash_report_type_t;
+
typedef struct
{
u8 mac[6];
u16 status;
u16 max_virtqueue_pairs;
u16 mtu;
+ u32 speed;
+ u8 duplex;
+ u8 rss_max_key_size;
+ u16 rss_max_indirection_table_length;
+ u32 supported_hash_types;
} virtio_net_config_t;
+#define VIRTIO_NET_RSS_MAX_TABLE_LEN 128
+#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
+
+#define foreach_virtio_net_hash_type \
+ _ (IPV4, 0) \
+ _ (TCPV4, 1) \
+ _ (UDPV4, 2) \
+ _ (IPV6, 3) \
+ _ (TCPV6, 4) \
+ _ (UDPV6, 5) \
+ _ (IPV6_EX, 6) \
+ _ (TCPV6_EX, 7) \
+ _ (UDPV6_EX, 8)
+
+typedef enum
+{
+#define _(n, i) VIRTIO_NET_HASH_TYPE_##n = (1 << i),
+ foreach_virtio_net_hash_type
+#undef _
+} virtio_net_hash_type_t;
+
+#define VIRTIO_NET_HASH_TYPE_SUPPORTED \
+ (VIRTIO_NET_HASH_TYPE_IPV4 | VIRTIO_NET_HASH_TYPE_TCPV4 | \
+ VIRTIO_NET_HASH_TYPE_UDPV4 | VIRTIO_NET_HASH_TYPE_IPV6 | \
+ VIRTIO_NET_HASH_TYPE_TCPV6 | VIRTIO_NET_HASH_TYPE_UDPV6 | \
+ VIRTIO_NET_HASH_TYPE_IPV6_EX | VIRTIO_NET_HASH_TYPE_TCPV6_EX | \
+ VIRTIO_NET_HASH_TYPE_UDPV6_EX)
+
+typedef struct
+{
+ u32 hash_types;
+ u16 indirection_table_mask;
+ u16 unclassified_queue;
+ u16 indirection_table[VIRTIO_NET_RSS_MAX_TABLE_LEN];
+ u16 max_tx_vq;
+ u8 hash_key_length;
+ u8 hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE];
+} virtio_net_rss_config;
+
/*
* Control virtqueue data structures
*
@@ -210,13 +276,14 @@ typedef struct _virtio_pci_func
void (*device_debug_config_space) (vlib_main_t * vm, virtio_if_t * vif);
} virtio_pci_func_t;
-#define foreach_virtio_flags \
- _ (GSO, 0) \
- _ (CSUM_OFFLOAD, 1) \
- _ (GRO_COALESCE, 2) \
- _ (PACKED, 3) \
- _ (IN_ORDER, 4) \
- _ (BUFFERING, 5)
+#define foreach_virtio_flags \
+ _ (GSO, 0) \
+ _ (CSUM_OFFLOAD, 1) \
+ _ (GRO_COALESCE, 2) \
+ _ (PACKED, 3) \
+ _ (IN_ORDER, 4) \
+ _ (BUFFERING, 5) \
+ _ (RSS, 6)
typedef enum
{
@@ -243,6 +310,7 @@ typedef struct
u64 features;
u8 gso_enabled;
u8 checksum_offload_enabled;
+ u8 rss_enabled;
u32 tx_queue_size;
virtio_bind_t bind;
u32 buffering_size;
diff --git a/src/vnet/devices/virtio/virtio.api b/src/vnet/devices/virtio/virtio.api
index a11492ec258..14a58491282 100644
--- a/src/vnet/devices/virtio/virtio.api
+++ b/src/vnet/devices/virtio/virtio.api
@@ -63,6 +63,7 @@ enumflag virtio_flags {
VIRTIO_API_FLAG_PACKED = 8, /* enable packed ring support, provided it is available from backend */
VIRTIO_API_FLAG_IN_ORDER = 16, /* enable in order support, provided it is available from backend */
VIRTIO_API_FLAG_BUFFERING = 32 [backwards_compatible], /* enable buffering to handle backend jitter/delays */
+ VIRTIO_API_FLAG_RSS = 64 [backwards_compatible], /* enable rss support */
};
/** \brief Initialize a new virtio pci interface with the given parameters
diff --git a/src/vnet/devices/virtio/virtio.c b/src/vnet/devices/virtio/virtio.c
index d2302fa1dc4..840936a43ff 100644
--- a/src/vnet/devices/virtio/virtio.c
+++ b/src/vnet/devices/virtio/virtio.c
@@ -404,6 +404,7 @@ virtio_show (vlib_main_t *vm, u32 *hw_if_indices, u8 show_descr,
vlib_cli_output (vm, " csum-enabled %d", vif->csum_offload_enabled);
vlib_cli_output (vm, " packet-coalesce %d", vif->packet_coalesce);
vlib_cli_output (vm, " packet-buffering %d", vif->packet_buffering);
+ vlib_cli_output (vm, " rss-enabled %d", vif->rss_enabled);
if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_PCI))
vlib_cli_output (vm, " Mac Address: %U", format_ethernet_address,
vif->mac_addr);
diff --git a/src/vnet/devices/virtio/virtio.h b/src/vnet/devices/virtio/virtio.h
index 431b1d25c26..a8e258884a4 100644
--- a/src/vnet/devices/virtio/virtio.h
+++ b/src/vnet/devices/virtio/virtio.h
@@ -140,6 +140,7 @@ typedef struct
vnet_virtio_vring_t *txq_vrings;
int gso_enabled;
int csum_offload_enabled;
+ int rss_enabled;
union
{
int *tap_fds;
diff --git a/src/vnet/devices/virtio/virtio_api.c b/src/vnet/devices/virtio/virtio_api.c
index 3197a2fab6d..2e25efff29e 100644
--- a/src/vnet/devices/virtio/virtio_api.c
+++ b/src/vnet/devices/virtio/virtio_api.c
@@ -111,18 +111,18 @@ vl_api_virtio_pci_create_v2_t_handler (vl_api_virtio_pci_create_v2_t * mp)
STATIC_ASSERT (((int) VIRTIO_API_FLAG_BUFFERING ==
(int) VIRTIO_FLAG_BUFFERING),
"virtio buffering api flag mismatch");
+ STATIC_ASSERT (((int) VIRTIO_API_FLAG_RSS == (int) VIRTIO_FLAG_RSS),
+ "virtio rss api flag mismatch");
ap->virtio_flags = clib_net_to_host_u32 (mp->virtio_flags);
ap->features = clib_net_to_host_u64 (mp->features);
if (ap->virtio_flags & VIRTIO_API_FLAG_GSO)
ap->gso_enabled = 1;
- else
- ap->gso_enabled = 0;
if (ap->virtio_flags & VIRTIO_API_FLAG_CSUM_OFFLOAD)
ap->checksum_offload_enabled = 1;
- else
- ap->checksum_offload_enabled = 0;
+ if (ap->virtio_flags & VIRTIO_API_FLAG_RSS)
+ ap->rss_enabled = 1;
virtio_pci_create_if (vm, ap);
diff --git a/src/vnet/devices/virtio/virtio_inline.h b/src/vnet/devices/virtio/virtio_inline.h
index 179f319aa4c..41bba755934 100644
--- a/src/vnet/devices/virtio/virtio_inline.h
+++ b/src/vnet/devices/virtio/virtio_inline.h
@@ -17,6 +17,7 @@
#define foreach_virtio_input_error \
_ (BUFFER_ALLOC, "buffer alloc error") \
+ _ (FULL_RX_QUEUE, "full rx queue (driver tx drop)") \
_ (UNKNOWN, "unknown")
typedef enum
diff --git a/src/vnet/devices/virtio/virtio_std.h b/src/vnet/devices/virtio/virtio_std.h
index ec988c08dbb..66b8bac092c 100644
--- a/src/vnet/devices/virtio/virtio_std.h
+++ b/src/vnet/devices/virtio/virtio_std.h
@@ -15,49 +15,61 @@
#ifndef __VIRTIO_STD_H__
#define __VIRTIO_STD_H__
-#define foreach_virtio_net_features \
- _ (VIRTIO_NET_F_CSUM, 0) /* Host handles pkts w/ partial csum */ \
- _ (VIRTIO_NET_F_GUEST_CSUM, 1) /* Guest handles pkts w/ partial csum */ \
- _ (VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, 2) /* Dynamic offload configuration. */ \
- _ (VIRTIO_NET_F_MTU, 3) /* Initial MTU advice. */ \
- _ (VIRTIO_NET_F_MAC, 5) /* Host has given MAC address. */ \
- _ (VIRTIO_NET_F_GSO, 6) /* Host handles pkts w/ any GSO. */ \
- _ (VIRTIO_NET_F_GUEST_TSO4, 7) /* Guest can handle TSOv4 in. */ \
- _ (VIRTIO_NET_F_GUEST_TSO6, 8) /* Guest can handle TSOv6 in. */ \
- _ (VIRTIO_NET_F_GUEST_ECN, 9) /* Guest can handle TSO[6] w/ ECN in. */ \
- _ (VIRTIO_NET_F_GUEST_UFO, 10) /* Guest can handle UFO in. */ \
- _ (VIRTIO_NET_F_HOST_TSO4, 11) /* Host can handle TSOv4 in. */ \
- _ (VIRTIO_NET_F_HOST_TSO6, 12) /* Host can handle TSOv6 in. */ \
- _ (VIRTIO_NET_F_HOST_ECN, 13) /* Host can handle TSO[6] w/ ECN in. */ \
- _ (VIRTIO_NET_F_HOST_UFO, 14) /* Host can handle UFO in. */ \
- _ (VIRTIO_NET_F_MRG_RXBUF, 15) /* Host can merge receive buffers. */ \
- _ (VIRTIO_NET_F_STATUS, 16) /* virtio_net_config.status available */ \
- _ (VIRTIO_NET_F_CTRL_VQ, 17) /* Control channel available */ \
- _ (VIRTIO_NET_F_CTRL_RX, 18) /* Control channel RX mode support */ \
- _ (VIRTIO_NET_F_CTRL_VLAN, 19) /* Control channel VLAN filtering */ \
- _ (VIRTIO_NET_F_CTRL_RX_EXTRA, 20) /* Extra RX mode control support */ \
- _ (VIRTIO_NET_F_GUEST_ANNOUNCE, 21) /* Guest can announce device on the network */ \
- _ (VIRTIO_NET_F_MQ, 22) /* Device supports Receive Flow Steering */ \
- _ (VIRTIO_NET_F_CTRL_MAC_ADDR, 23) /* Set MAC address */ \
- _ (VIRTIO_F_NOTIFY_ON_EMPTY, 24) \
- _ (VHOST_F_LOG_ALL, 26) /* Log all write descriptors */ \
- _ (VIRTIO_F_ANY_LAYOUT, 27) /* Can the device handle any descriptor layout */ \
- _ (VIRTIO_RING_F_INDIRECT_DESC, 28) /* Support indirect buffer descriptors */ \
- _ (VIRTIO_RING_F_EVENT_IDX, 29) /* The Guest publishes the used index for which it expects an interrupt \
- * at the end of the avail ring. Host should ignore the avail->flags field. */ \
-/* The Host publishes the avail index for which it expects a kick \
- * at the end of the used ring. Guest should ignore the used->flags field. */ \
- _ (VHOST_USER_F_PROTOCOL_FEATURES, 30) \
- _ (VIRTIO_F_VERSION_1, 32) /* v1.0 compliant. */ \
- _ (VIRTIO_F_IOMMU_PLATFORM, 33) \
- _ (VIRTIO_F_RING_PACKED, 34) \
- _ (VIRTIO_F_IN_ORDER, 35) /* all buffers are used by the device in the */ \
- /* same order in which they have been made available */ \
+#define foreach_virtio_net_features \
+ _ (VIRTIO_NET_F_CSUM, 0) /* Host handles pkts w/ partial csum */ \
+ _ (VIRTIO_NET_F_GUEST_CSUM, 1) /* Guest handles pkts w/ partial csum */ \
+ _ (VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \
+ 2) /* Dynamic offload configuration. */ \
+ _ (VIRTIO_NET_F_MTU, 3) /* Initial MTU advice. */ \
+ _ (VIRTIO_NET_F_MAC, 5) /* Host has given MAC address. */ \
+ _ (VIRTIO_NET_F_GSO, 6) /* Host handles pkts w/ any GSO. */ \
+ _ (VIRTIO_NET_F_GUEST_TSO4, 7) /* Guest can handle TSOv4 in. */ \
+ _ (VIRTIO_NET_F_GUEST_TSO6, 8) /* Guest can handle TSOv6 in. */ \
+ _ (VIRTIO_NET_F_GUEST_ECN, 9) /* Guest can handle TSO[6] w/ ECN in. */ \
+ _ (VIRTIO_NET_F_GUEST_UFO, 10) /* Guest can handle UFO in. */ \
+ _ (VIRTIO_NET_F_HOST_TSO4, 11) /* Host can handle TSOv4 in. */ \
+ _ (VIRTIO_NET_F_HOST_TSO6, 12) /* Host can handle TSOv6 in. */ \
+ _ (VIRTIO_NET_F_HOST_ECN, 13) /* Host can handle TSO[6] w/ ECN in. */ \
+ _ (VIRTIO_NET_F_HOST_UFO, 14) /* Host can handle UFO in. */ \
+ _ (VIRTIO_NET_F_MRG_RXBUF, 15) /* Host can merge receive buffers. */ \
+ _ (VIRTIO_NET_F_STATUS, 16) /* virtio_net_config.status available */ \
+ _ (VIRTIO_NET_F_CTRL_VQ, 17) /* Control channel available */ \
+ _ (VIRTIO_NET_F_CTRL_RX, 18) /* Control channel RX mode support */ \
+ _ (VIRTIO_NET_F_CTRL_VLAN, 19) /* Control channel VLAN filtering */ \
+ _ (VIRTIO_NET_F_CTRL_RX_EXTRA, 20) /* Extra RX mode control support */ \
+ _ (VIRTIO_NET_F_GUEST_ANNOUNCE, \
+ 21) /* Guest can announce device on the network */ \
+ _ (VIRTIO_NET_F_MQ, 22) /* Device supports Receive Flow Steering */ \
+ _ (VIRTIO_NET_F_CTRL_MAC_ADDR, 23) /* Set MAC address */ \
+ _ (VIRTIO_F_NOTIFY_ON_EMPTY, 24) \
+ _ (VHOST_F_LOG_ALL, 26) /* Log all write descriptors */ \
+ _ (VIRTIO_F_ANY_LAYOUT, \
+ 27) /* Can the device handle any descriptor layout */ \
+ _ (VIRTIO_RING_F_INDIRECT_DESC, \
+ 28) /* Support indirect buffer descriptors */ \
+ _ (VIRTIO_RING_F_EVENT_IDX, \
+ 29) /* The Guest publishes the used index for which it expects an \
+ * interrupt at the end of the avail ring. Host should ignore the \
+ * avail->flags field. */ \
+ /* The Host publishes the avail index for which it expects a kick \
+ * at the end of the used ring. Guest should ignore the used->flags field. \
+ */ \
+ _ (VHOST_USER_F_PROTOCOL_FEATURES, 30) \
+ _ (VIRTIO_F_VERSION_1, 32) /* v1.0 compliant. */ \
+ _ (VIRTIO_F_IOMMU_PLATFORM, 33) \
+ _ (VIRTIO_F_RING_PACKED, 34) \
+ _ (VIRTIO_F_IN_ORDER, 35) /* all buffers are used by the device in the */ \
+ /* same order in which they have been made available */ \
_ (VIRTIO_F_ORDER_PLATFORM, 36) /* memory accesses by the driver and the */ \
- /* device are ordered in a way described by the platfor */ \
- _ (VIRTIO_F_NOTIFICATION_DATA, 38) /* the driver passes extra data (besides */ \
- /* identifying the virtqueue) in its device notifications. */ \
- _ (VIRTIO_NET_F_SPEED_DUPLEX, 63) /* Device set linkspeed and duplex */
+ /* device are ordered in a way described by the platfor */ \
+ _ (VIRTIO_F_NOTIFICATION_DATA, \
+ 38) /* the driver passes extra data (besides */ \
+ /* identifying the virtqueue) in its device notifications. */ \
+ _ (VIRTIO_NET_F_RING_RESET, 40) /* Device supports individual ring reset */ \
+ _ (VIRTIO_NET_F_HASH_REPORT, \
+ 57) /* Device supports per packet hash value */ \
+ _ (VIRTIO_NET_F_RSS, 60) /* Device supports RSS */ \
+ _ (VIRTIO_NET_F_SPEED_DUPLEX, 63) /* Device set linkspeed and duplex */
typedef enum
{
diff --git a/src/vnet/error.c b/src/vnet/error.c
index 473d11135f1..7ae32962132 100644
--- a/src/vnet/error.c
+++ b/src/vnet/error.c
@@ -37,6 +37,10 @@ u8 *
format_vnet_api_errno (u8 *s, va_list *args)
{
vnet_api_error_t api_error = va_arg (*args, vnet_api_error_t);
+
+ if (0 == api_error)
+ return format (s, "Success");
+
#ifdef _
#undef _
#endif
diff --git a/src/vnet/fib/fib_api.c b/src/vnet/fib/fib_api.c
index 07d6699d87a..426161b0670 100644
--- a/src/vnet/fib/fib_api.c
+++ b/src/vnet/fib/fib_api.c
@@ -190,6 +190,7 @@ fib_api_path_decode (vl_api_fib_path_t *in,
break;
case FIB_API_PATH_TYPE_DROP:
out->frp_flags |= FIB_ROUTE_PATH_DROP;
+ out->frp_sw_if_index = ntohl(in->sw_if_index);
break;
case FIB_API_PATH_TYPE_LOCAL:
out->frp_flags |= FIB_ROUTE_PATH_LOCAL;
@@ -493,6 +494,21 @@ fib_api_route_add_del (u8 is_add,
return (0);
}
+u8 *
+format_vl_api_address_union (u8 * s, va_list * args)
+{
+ const vl_api_address_union_t *addr =
+ va_arg (*args, vl_api_address_union_t *);
+ vl_api_address_family_t af = va_arg (*args, int);
+
+ if (ADDRESS_IP6 == af)
+ s = format (s, "%U", format_ip6_address, addr->ip6);
+ else
+ s = format (s, "%U", format_ip4_address, addr->ip4);
+
+ return s;
+}
+
u8*
format_vl_api_fib_path (u8 * s, va_list * args)
{
diff --git a/src/vnet/fib/fib_api.h b/src/vnet/fib/fib_api.h
index 0c59531b438..9eac62fe1c6 100644
--- a/src/vnet/fib/fib_api.h
+++ b/src/vnet/fib/fib_api.h
@@ -51,6 +51,7 @@ extern int fib_api_route_add_del (u8 is_add,
fib_entry_flag_t entry_flags,
fib_route_path_t *rpaths);
+extern u8 *format_vl_api_address_union (u8 * s, va_list * args);
extern u8* format_vl_api_fib_path(u8 * s, va_list * args);
diff --git a/src/vnet/fib/fib_entry.c b/src/vnet/fib/fib_entry.c
index b78346ce45a..adf880b8bbb 100644
--- a/src/vnet/fib/fib_entry.c
+++ b/src/vnet/fib/fib_entry.c
@@ -1828,10 +1828,18 @@ fib_entry_pool_size (void)
return (pool_elts(fib_entry_pool));
}
-#if CLIB_DEBUG > 0
void
fib_table_assert_empty (const fib_table_t *fib_table)
{
+ if (0 == fib_table->ft_total_route_counts)
+ return;
+
+ vlib_log_err (fib_entry_logger,
+ "BUG: %U table %d (index %d) is not empty",
+ format_fib_protocol, fib_table->ft_proto,
+ fib_table->ft_table_id, fib_table->ft_index);
+
+#if CLIB_DEBUG > 0
fib_node_index_t *fei, *feis = NULL;
fib_entry_t *fib_entry;
@@ -1848,8 +1856,8 @@ fib_table_assert_empty (const fib_table_t *fib_table)
}
ASSERT(0);
-}
#endif
+}
static clib_error_t *
show_fib_entry_command (vlib_main_t * vm,
diff --git a/src/vnet/fib/fib_entry_src.c b/src/vnet/fib/fib_entry_src.c
index c79b745b5b5..d9cf82343c5 100644
--- a/src/vnet/fib/fib_entry_src.c
+++ b/src/vnet/fib/fib_entry_src.c
@@ -875,8 +875,12 @@ fib_entry_src_covered_inherit_add_i (fib_entry_t *fib_entry,
* The covered's source data has been inherited, presumably
* from this cover, i.e. this is a modify.
*/
- esrc = fib_entry_src_action_update_from_cover(fib_entry, cover_src);
- fib_entry_source_change(fib_entry, esrc->fes_src, esrc->fes_src);
+ fib_source_t best_source;
+
+ best_source = fib_entry_get_best_source(
+ fib_entry_get_index(fib_entry));
+ fib_entry_src_action_update_from_cover(fib_entry, cover_src);
+ fib_entry_source_change(fib_entry, best_source, cover_src->fes_src);
}
else
{
@@ -1538,6 +1542,10 @@ fib_entry_flags_update (const fib_entry_t *fib_entry,
{
esrc->fes_entry_flags |= FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT;
}
+ if (rpath->frp_flags & FIB_ROUTE_PATH_DROP)
+ {
+ esrc->fes_entry_flags |= FIB_ENTRY_FLAG_NO_ATTACHED_EXPORT;
+ }
}
if (fib_route_attached_cross_table(fib_entry, rpath) &&
!(esrc->fes_entry_flags & FIB_ENTRY_FLAG_NO_ATTACHED_EXPORT))
diff --git a/src/vnet/fib/fib_table.h b/src/vnet/fib/fib_table.h
index 0eaaa67eea2..d78c9e619b8 100644
--- a/src/vnet/fib/fib_table.h
+++ b/src/vnet/fib/fib_table.h
@@ -980,9 +980,7 @@ extern u8 *format_fib_table_memory(u8 *s, va_list *args);
/**
* Debug function
*/
-#if CLIB_DEBUG > 0
extern void fib_table_assert_empty(const fib_table_t *fib_table);
-#endif
#endif
diff --git a/src/vnet/fib/fib_types.c b/src/vnet/fib/fib_types.c
index c4472c7122d..9abb89bc6a0 100644
--- a/src/vnet/fib/fib_types.c
+++ b/src/vnet/fib/fib_types.c
@@ -715,6 +715,10 @@ unformat_fib_route_path (unformat_input_t * input, va_list * args)
rpath->frp_proto = DPO_PROTO_IP6;
rpath->frp_flags = FIB_ROUTE_PATH_INTF_RX;
}
+ else if (unformat (input, "drop"))
+ {
+ rpath->frp_flags = FIB_ROUTE_PATH_DROP;
+ }
else if (unformat (input, "local"))
{
clib_memset (&rpath->frp_addr, 0, sizeof (rpath->frp_addr));
diff --git a/src/vnet/fib/ip4_fib.c b/src/vnet/fib/ip4_fib.c
index 0eff8d0d485..23f70a770bf 100644
--- a/src/vnet/fib/ip4_fib.c
+++ b/src/vnet/fib/ip4_fib.c
@@ -201,10 +201,7 @@ ip4_fib_table_destroy (u32 fib_index)
/*
* validate no more routes.
*/
-#if CLIB_DEBUG > 0
- if (0 != fib_table->ft_total_route_counts)
- fib_table_assert_empty(fib_table);
-#endif
+ fib_table_assert_empty(fib_table);
vec_foreach(n_locks, fib_table->ft_src_route_counts)
{
diff --git a/src/vnet/fib/ip6_fib.c b/src/vnet/fib/ip6_fib.c
index d37b77e08a4..f844cfaa420 100644
--- a/src/vnet/fib/ip6_fib.c
+++ b/src/vnet/fib/ip6_fib.c
@@ -20,14 +20,20 @@
#include <vppinfra/bihash_24_8.h>
#include <vppinfra/bihash_template.c>
-ip6_fib_table_instance_t ip6_fib_table[IP6_FIB_NUM_TABLES];
+ip6_fib_fwding_table_instance_t ip6_fib_fwding_table;
/* ip6 lookup table config parameters */
u32 ip6_fib_table_nbuckets;
uword ip6_fib_table_size;
+typedef struct ip6_fib_hash_key_t_
+{
+ ip6_address_t addr;
+ u8 len;
+} ip6_fib_hash_key_t;
+
static void
-vnet_ip6_fib_init (u32 fib_index)
+ip6_fib_hash_load_specials (u32 fib_index)
{
fib_prefix_t pfx = {
.fp_proto = FIB_PROTOCOL_IP6,
@@ -77,7 +83,7 @@ create_fib_with_table_id (u32 table_id,
ASSERT((fib_table - ip6_main.fibs) ==
(v6_fib - ip6_main.v6_fibs));
-
+
fib_table->ft_proto = FIB_PROTOCOL_IP6;
fib_table->ft_index =
v6_fib->index =
@@ -92,9 +98,15 @@ create_fib_with_table_id (u32 table_id,
fib_table->ft_flags = flags;
fib_table->ft_desc = desc;
- vnet_ip6_fib_init(fib_table->ft_index);
fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP6, src);
+ v6_fib->fib_entry_by_dst_address = hash_create_mem(2, sizeof(ip6_fib_hash_key_t), sizeof(fib_node_index_t));
+
+ /*
+ * add the special entries into the new FIB
+ */
+ ip6_fib_hash_load_specials (fib_table->ft_index);
+
return (fib_table->ft_index);
}
@@ -160,10 +172,7 @@ ip6_fib_table_destroy (u32 fib_index)
/*
* validate no more routes.
*/
-#if CLIB_DEBUG > 0
- if (0 != fib_table->ft_total_route_counts)
- fib_table_assert_empty(fib_table);
-#endif
+ fib_table_assert_empty(fib_table);
vec_foreach_index(source, fib_table->ft_src_route_counts)
{
@@ -176,54 +185,40 @@ ip6_fib_table_destroy (u32 fib_index)
}
vec_free (fib_table->ft_locks);
vec_free(fib_table->ft_src_route_counts);
+ hash_free(pool_elt_at_index(ip6_main.v6_fibs, fib_index)->fib_entry_by_dst_address);
pool_put_index(ip6_main.v6_fibs, fib_table->ft_index);
pool_put(ip6_main.fibs, fib_table);
}
+static void
+ip6_fib_table_mk_key (ip6_fib_hash_key_t *key, const ip6_address_t *addr, u8 len)
+{
+ const ip6_address_t *mask = &ip6_main.fib_masks[len];
+ key->addr.as_u64[0] = addr->as_u64[0] & mask->as_u64[0];
+ key->addr.as_u64[1] = addr->as_u64[1] & mask->as_u64[1];
+ key->len = len;
+}
+
fib_node_index_t
ip6_fib_table_lookup (u32 fib_index,
const ip6_address_t *addr,
u32 len)
{
- ip6_fib_table_instance_t *table;
- clib_bihash_kv_24_8_t kv, value;
- int i, n_p, rv;
- u64 fib;
-
- table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
- n_p = vec_len (table->prefix_lengths_in_search_order);
-
- kv.key[0] = addr->as_u64[0];
- kv.key[1] = addr->as_u64[1];
- fib = ((u64)((fib_index))<<32);
+ uword *hash = pool_elt_at_index(ip6_main.v6_fibs, fib_index)->fib_entry_by_dst_address;
+ ip6_fib_hash_key_t key;
+ i32 mask_len;
+ uword *result;
- /*
- * start search from a mask length same length or shorter.
- * we don't want matches longer than the mask passed
- */
- i = 0;
- while (i < n_p && table->prefix_lengths_in_search_order[i] > len)
+ for (mask_len = len; mask_len >= 0; mask_len--)
{
- i++;
+ ip6_fib_table_mk_key (&key, addr, mask_len);
+ result = hash_get_mem(hash, &key);
+ if (result) {
+ return result[0];
+ }
}
- for (; i < n_p; i++)
- {
- int dst_address_length = table->prefix_lengths_in_search_order[i];
- ip6_address_t * mask = &ip6_main.fib_masks[dst_address_length];
-
- ASSERT(dst_address_length >= 0 && dst_address_length <= 128);
- //As lengths are decreasing, masks are increasingly specific.
- kv.key[0] &= mask->as_u64[0];
- kv.key[1] &= mask->as_u64[1];
- kv.key[2] = fib | dst_address_length;
-
- rv = clib_bihash_search_inline_2_24_8(&table->ip6_hash, &kv, &value);
- if (rv == 0)
- return value.value;
- }
-
- return (FIB_NODE_INDEX_INVALID);
+ return FIB_NODE_INDEX_INVALID;
}
fib_node_index_t
@@ -231,53 +226,11 @@ ip6_fib_table_lookup_exact_match (u32 fib_index,
const ip6_address_t *addr,
u32 len)
{
- ip6_fib_table_instance_t *table;
- clib_bihash_kv_24_8_t kv, value;
- ip6_address_t *mask;
- u64 fib;
- int rv;
-
- table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
- mask = &ip6_main.fib_masks[len];
- fib = ((u64)((fib_index))<<32);
-
- kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
- kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
- kv.key[2] = fib | len;
-
- rv = clib_bihash_search_inline_2_24_8(&table->ip6_hash, &kv, &value);
- if (rv == 0)
- return value.value;
-
- return (FIB_NODE_INDEX_INVALID);
-}
-
-static void
-compute_prefix_lengths_in_search_order (ip6_fib_table_instance_t *table)
-{
- u8 *old, *prefix_lengths_in_search_order = NULL;
- int i;
-
- /*
- * build the list in a scratch space then cutover so the workers
- * can continue uninterrupted.
- */
- old = table->prefix_lengths_in_search_order;
-
- /* Note: bitmap reversed so this is in fact a longest prefix match */
- clib_bitmap_foreach (i, table->non_empty_dst_address_length_bitmap)
- {
- int dst_address_length = 128 - i;
- vec_add1(prefix_lengths_in_search_order, dst_address_length);
- }
-
- table->prefix_lengths_in_search_order = prefix_lengths_in_search_order;
-
- /*
- * let the workers go once round the track before we free the old set
- */
- vlib_worker_wait_one_loop();
- vec_free(old);
+ uword *hash = pool_elt_at_index(ip6_main.v6_fibs, fib_index)->fib_entry_by_dst_address;
+ ip6_fib_hash_key_t key;
+ ip6_fib_table_mk_key (&key, addr, len);
+ uword *result = hash_get(hash, &key);
+ return result ? result[0] : FIB_NODE_INDEX_INVALID;
}
void
@@ -285,30 +238,10 @@ ip6_fib_table_entry_remove (u32 fib_index,
const ip6_address_t *addr,
u32 len)
{
- ip6_fib_table_instance_t *table;
- clib_bihash_kv_24_8_t kv;
- ip6_address_t *mask;
- u64 fib;
-
- table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
- mask = &ip6_main.fib_masks[len];
- fib = ((u64)((fib_index))<<32);
-
- kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
- kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
- kv.key[2] = fib | len;
-
- clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);
-
- /* refcount accounting */
- ASSERT (table->dst_address_length_refcounts[len] > 0);
- if (--table->dst_address_length_refcounts[len] == 0)
- {
- table->non_empty_dst_address_length_bitmap =
- clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
- 128 - len, 0);
- compute_prefix_lengths_in_search_order (table);
- }
+ uword **hash = &pool_elt_at_index(ip6_main.v6_fibs, fib_index)->fib_entry_by_dst_address;
+ ip6_fib_hash_key_t key;
+ ip6_fib_table_mk_key (&key, addr, len);
+ hash_unset_mem_free(hash, &key);
}
void
@@ -317,29 +250,11 @@ ip6_fib_table_entry_insert (u32 fib_index,
u32 len,
fib_node_index_t fib_entry_index)
{
- ip6_fib_table_instance_t *table;
- clib_bihash_kv_24_8_t kv;
- ip6_address_t *mask;
- u64 fib;
-
- table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
- mask = &ip6_main.fib_masks[len];
- fib = ((u64)((fib_index))<<32);
-
- kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
- kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
- kv.key[2] = fib | len;
- kv.value = fib_entry_index;
-
- clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);
-
- if (0 == table->dst_address_length_refcounts[len]++)
- {
- table->non_empty_dst_address_length_bitmap =
- clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
- 128 - len, 1);
- compute_prefix_lengths_in_search_order (table);
- }
+ uword **hash = &pool_elt_at_index(ip6_main.v6_fibs, fib_index)->fib_entry_by_dst_address;
+ ip6_fib_hash_key_t key;
+ ip6_fib_table_mk_key (&key, addr, len);
+ ASSERT (0 == hash_get(*hash, &key) && "entry already exists");
+ hash_set_mem_alloc(hash, &key, fib_entry_index);
}
u32 ip6_fib_table_fwding_lookup_with_if_index (ip6_main_t * im,
@@ -364,18 +279,46 @@ ip6_fib_table_get_index_for_sw_if_index (u32 sw_if_index)
return (ip6_main.fib_index_by_sw_if_index[sw_if_index]);
}
+static void
+compute_prefix_lengths_in_search_order (ip6_fib_fwding_table_instance_t *table)
+{
+ u8 *old, *prefix_lengths_in_search_order = NULL;
+ int i;
+
+ /*
+ * build the list in a scratch space then cutover so the workers
+ * can continue uninterrupted.
+ */
+ old = table->prefix_lengths_in_search_order;
+
+ /* Note: bitmap reversed so this is in fact a longest prefix match */
+ clib_bitmap_foreach (i, table->non_empty_dst_address_length_bitmap)
+ {
+ int dst_address_length = 128 - i;
+ vec_add1(prefix_lengths_in_search_order, dst_address_length);
+ }
+
+ table->prefix_lengths_in_search_order = prefix_lengths_in_search_order;
+
+ /*
+ * let the workers go once round the track before we free the old set
+ */
+ vlib_worker_wait_one_loop();
+ vec_free(old);
+}
+
void
ip6_fib_table_fwding_dpo_update (u32 fib_index,
const ip6_address_t *addr,
u32 len,
const dpo_id_t *dpo)
{
- ip6_fib_table_instance_t *table;
+ ip6_fib_fwding_table_instance_t *table;
clib_bihash_kv_24_8_t kv;
ip6_address_t *mask;
u64 fib;
- table = &ip6_fib_table[IP6_FIB_TABLE_FWDING];
+ table = &ip6_fib_fwding_table;
mask = &ip6_main.fib_masks[len];
fib = ((u64)((fib_index))<<32);
@@ -401,12 +344,12 @@ ip6_fib_table_fwding_dpo_remove (u32 fib_index,
u32 len,
const dpo_id_t *dpo)
{
- ip6_fib_table_instance_t *table;
+ ip6_fib_fwding_table_instance_t *table;
clib_bihash_kv_24_8_t kv;
ip6_address_t *mask;
u64 fib;
- table = &ip6_fib_table[IP6_FIB_TABLE_FWDING];
+ table = &ip6_fib_fwding_table;
mask = &ip6_main.fib_masks[len];
fib = ((u64)((fib_index))<<32);
@@ -428,101 +371,18 @@ ip6_fib_table_fwding_dpo_remove (u32 fib_index,
}
}
-/**
- * @brief Context when walking the IPv6 table. Since all VRFs are in the
- * same hash table, we need to filter only those we need as we walk
- */
-typedef struct ip6_fib_walk_ctx_t_
-{
- u32 i6w_fib_index;
- fib_table_walk_fn_t i6w_fn;
- void *i6w_ctx;
- fib_prefix_t i6w_root;
- fib_prefix_t *i6w_sub_trees;
-} ip6_fib_walk_ctx_t;
-
-static int
-ip6_fib_walk_cb (clib_bihash_kv_24_8_t * kvp,
- void *arg)
-{
- ip6_fib_walk_ctx_t *ctx = arg;
- ip6_address_t key;
-
- if ((kvp->key[2] >> 32) == ctx->i6w_fib_index)
- {
- key.as_u64[0] = kvp->key[0];
- key.as_u64[1] = kvp->key[1];
-
- if (ip6_destination_matches_route(&ip6_main,
- &key,
- &ctx->i6w_root.fp_addr.ip6,
- ctx->i6w_root.fp_len))
- {
- const fib_prefix_t *sub_tree;
- int skip = 0;
-
- /*
- * exclude sub-trees the walk does not want to explore
- */
- vec_foreach(sub_tree, ctx->i6w_sub_trees)
- {
- if (ip6_destination_matches_route(&ip6_main,
- &key,
- &sub_tree->fp_addr.ip6,
- sub_tree->fp_len))
- {
- skip = 1;
- break;
- }
- }
-
- if (!skip)
- {
- switch (ctx->i6w_fn(kvp->value, ctx->i6w_ctx))
- {
- case FIB_TABLE_WALK_CONTINUE:
- break;
- case FIB_TABLE_WALK_SUB_TREE_STOP: {
- fib_prefix_t pfx = {
- .fp_proto = FIB_PROTOCOL_IP6,
- .fp_len = kvp->key[2] & 0xffffffff,
- .fp_addr.ip6 = key,
- };
- vec_add1(ctx->i6w_sub_trees, pfx);
- break;
- }
- case FIB_TABLE_WALK_STOP:
- goto done;
- }
- }
- }
- }
-done:
-
- return (1);
-}
-
void
ip6_fib_table_walk (u32 fib_index,
fib_table_walk_fn_t fn,
void *arg)
{
- ip6_fib_walk_ctx_t ctx = {
- .i6w_fib_index = fib_index,
- .i6w_fn = fn,
- .i6w_ctx = arg,
- .i6w_root = {
- .fp_proto = FIB_PROTOCOL_IP6,
- },
- .i6w_sub_trees = NULL,
+ const fib_prefix_t root = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ // address and length default to all 0
};
-
- clib_bihash_foreach_key_value_pair_24_8(
- &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
- ip6_fib_walk_cb,
- &ctx);
-
- vec_free(ctx.i6w_sub_trees);
+ /* A full tree walk is the dengenerate case of a sub-tree from
+ * the very root */
+ return (ip6_fib_table_sub_tree_walk(fib_index, &root, fn, arg));
}
void
@@ -531,17 +391,43 @@ ip6_fib_table_sub_tree_walk (u32 fib_index,
fib_table_walk_fn_t fn,
void *arg)
{
- ip6_fib_walk_ctx_t ctx = {
- .i6w_fib_index = fib_index,
- .i6w_fn = fn,
- .i6w_ctx = arg,
- .i6w_root = *root,
- };
+ uword *hash = pool_elt_at_index(ip6_main.v6_fibs, fib_index)->fib_entry_by_dst_address;
+ const ip6_fib_hash_key_t *key, *sub_tree;
+ ip6_fib_hash_key_t *sub_trees = 0;
+ u32 fei;
- clib_bihash_foreach_key_value_pair_24_8(
- &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
- ip6_fib_walk_cb,
- &ctx);
+ /*
+ * There is no efficient way to walk this hash table.
+ * so we walk over all entries and check it is covered by the root.
+ */
+ hash_foreach_mem(key, fei, hash, ({
+ /* check if the prefix is covered by the root */
+ if (!ip6_destination_matches_route(&ip6_main, &key->addr, &root->fp_addr.ip6, root->fp_len))
+ continue; /* not covered by root, ignore */
+
+ /* exclude sub-trees the walk does not want to explore */
+ vec_foreach (sub_tree, sub_trees)
+ {
+ if (ip6_destination_matches_route(&ip6_main, &key->addr, &sub_tree->addr, sub_tree->len))
+ goto ignore_sub_tree;
+ }
+
+ switch (fn(fei, arg))
+ {
+ case FIB_TABLE_WALK_STOP:
+ goto done;
+ case FIB_TABLE_WALK_CONTINUE:
+ break;
+ case FIB_TABLE_WALK_SUB_TREE_STOP:
+ vec_add1(sub_trees, *key);
+ break;
+ }
+
+ignore_sub_tree:;
+ }));
+
+done:
+ vec_free(sub_trees);
}
typedef struct ip6_fib_show_ctx_t_ {
@@ -602,8 +488,7 @@ format_ip6_fib_table_memory (u8 * s, va_list * args)
{
uword bytes_inuse;
- bytes_inuse = (alloc_arena_next(&(ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash)) +
- alloc_arena_next(&(ip6_fib_table[IP6_FIB_TABLE_FWDING].ip6_hash)));
+ bytes_inuse = alloc_arena_next(&ip6_fib_fwding_table.ip6_hash);
s = format(s, "%=30s %=6d %=12ld\n",
"IPv6 unicast",
@@ -612,26 +497,60 @@ format_ip6_fib_table_memory (u8 * s, va_list * args)
return (s);
}
-typedef struct {
- u32 fib_index;
- u64 count_by_prefix_length[129];
-} count_routes_in_fib_at_prefix_length_arg_t;
-
-static int
-count_routes_in_fib_at_prefix_length (clib_bihash_kv_24_8_t * kvp,
- void *arg)
+void
+ip6_fib_table_show (vlib_main_t *vm, fib_table_t *fib_table, int summary)
{
- count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
- int mask_width;
+ ip6_main_t * im6 = &ip6_main;
+ ip6_fib_t *fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index);
+ fib_source_t source;
+ u8 *s = NULL;
+
+ s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
+ format_fib_table_name, fib->index,
+ FIB_PROTOCOL_IP6,
+ fib->index,
+ format_ip_flow_hash_config,
+ fib_table->ft_flow_hash_config,
+ fib_table->ft_epoch,
+ format_fib_table_flags, fib_table->ft_flags);
+
+ vec_foreach_index(source, fib_table->ft_locks)
+ {
+ if (0 != fib_table->ft_locks[source])
+ {
+ s = format(s, "%U:%d, ",
+ format_fib_source, source,
+ fib_table->ft_locks[source]);
+ }
+ }
+ s = format (s, "]");
+ vlib_cli_output (vm, "%v", s);
+ vec_free(s);
- if ((kvp->key[2]>>32) != ap->fib_index)
- return (BIHASH_WALK_CONTINUE);
+ /* Show summary? */
+ if (summary)
+ {
+ u32 count_by_prefix_length[129];
+ const ip6_fib_hash_key_t *key;
+ u32 fei;
+ int len;
- mask_width = kvp->key[2] & 0xFF;
+ vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
- ap->count_by_prefix_length[mask_width]++;
+ clib_memset (count_by_prefix_length, 0, sizeof(count_by_prefix_length));
- return (BIHASH_WALK_CONTINUE);
+ hash_foreach_mem(key, fei, fib->fib_entry_by_dst_address, ({
+ ASSERT(key->len <= 128);
+ count_by_prefix_length[key->len]++;
+ }));
+
+ for (len = 128; len >= 0; len--)
+ {
+ if (count_by_prefix_length[len])
+ vlib_cli_output (vm, "%=20d%=16lld",
+ len, count_by_prefix_length[len]);
+ }
+ }
}
static clib_error_t *
@@ -639,7 +558,6 @@ ip6_show_fib (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
- count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca;
ip6_main_t * im6 = &ip6_main;
fib_table_t *fib_table;
ip6_fib_t * fib;
@@ -686,22 +604,15 @@ ip6_show_fib (vlib_main_t * vm,
if (hash)
{
- vlib_cli_output (vm, "IPv6 Non-Forwarding Hash Table:\n%U\n",
- BV (format_bihash),
- &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
- detail);
vlib_cli_output (vm, "IPv6 Forwarding Hash Table:\n%U\n",
BV (format_bihash),
- &ip6_fib_table[IP6_FIB_TABLE_FWDING].ip6_hash,
+ &ip6_fib_fwding_table.ip6_hash,
detail);
return (NULL);
}
pool_foreach (fib_table, im6->fibs)
{
- fib_source_t source;
- u8 *s = NULL;
-
fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index);
if (table_id >= 0 && table_id != (int)fib->table_id)
continue;
@@ -710,50 +621,9 @@ ip6_show_fib (vlib_main_t * vm,
if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL)
continue;
- s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
- format_fib_table_name, fib->index,
- FIB_PROTOCOL_IP6,
- fib->index,
- format_ip_flow_hash_config,
- fib_table->ft_flow_hash_config,
- fib_table->ft_epoch,
- format_fib_table_flags, fib_table->ft_flags);
-
- vec_foreach_index(source, fib_table->ft_locks)
- {
- if (0 != fib_table->ft_locks[source])
- {
- s = format(s, "%U:%d, ",
- format_fib_source, source,
- fib_table->ft_locks[source]);
- }
- }
- s = format (s, "]");
- vlib_cli_output (vm, "%v", s);
- vec_free(s);
-
- /* Show summary? */
- if (! verbose)
- {
- clib_bihash_24_8_t * h = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash;
- int len;
-
- vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
-
- clib_memset (ca, 0, sizeof(*ca));
- ca->fib_index = fib->index;
-
- clib_bihash_foreach_key_value_pair_24_8
- (h, count_routes_in_fib_at_prefix_length, ca);
-
- for (len = 128; len >= 0; len--)
- {
- if (ca->count_by_prefix_length[len])
- vlib_cli_output (vm, "%=20d%=16lld",
- len, ca->count_by_prefix_length[len]);
- }
- continue;
- }
+ ip6_fib_table_show(vm, fib_table, !verbose);
+ if (!verbose)
+ continue;
if (!matching)
{
@@ -909,12 +779,9 @@ ip6_fib_init (vlib_main_t * vm)
if (ip6_fib_table_size == 0)
ip6_fib_table_size = IP6_FIB_DEFAULT_HASH_MEMORY_SIZE;
- clib_bihash_init_24_8 (&(ip6_fib_table[IP6_FIB_TABLE_FWDING].ip6_hash),
+ clib_bihash_init_24_8 (&(ip6_fib_fwding_table.ip6_hash),
"ip6 FIB fwding table",
ip6_fib_table_nbuckets, ip6_fib_table_size);
- clib_bihash_init_24_8 (&ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
- "ip6 FIB non-fwding table",
- ip6_fib_table_nbuckets, ip6_fib_table_size);
return (NULL);
}
diff --git a/src/vnet/fib/ip6_fib.h b/src/vnet/fib/ip6_fib.h
index 706bebbce09..80d56b1a7ba 100644
--- a/src/vnet/fib/ip6_fib.h
+++ b/src/vnet/fib/ip6_fib.h
@@ -26,36 +26,15 @@
#include <vppinfra/bihash_template.h>
/*
- * Default size of the ip6 fib hash table
+ * Default size of the ip6 fib forwarding hash table
*/
#define IP6_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
#define IP6_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20)
/**
- * Enumeration of the FIB table instance types
+ * A representation the forwarding IP6 table
*/
-typedef enum ip6_fib_table_instance_type_t_
-{
- /**
- * This table stores the routes that are used to forward traffic.
- * The key is the prefix, the result the adjacency to forward on.
- */
- IP6_FIB_TABLE_FWDING,
- /**
- * The table that stores ALL routes learned by the DP.
- * Some of these routes may not be ready to install in forwarding
- * at a given time.
- * The key in this table is the prefix, the result is the fib_entry_t
- */
- IP6_FIB_TABLE_NON_FWDING,
-} ip6_fib_table_instance_type_t;
-
-#define IP6_FIB_NUM_TABLES (IP6_FIB_TABLE_NON_FWDING+1)
-
-/**
- * A representation of a single IP6 table
- */
-typedef struct ip6_fib_table_instance_t_
+typedef struct ip6_fib_fwding_table_instance_t_
{
/* The hash table */
clib_bihash_24_8_t ip6_hash;
@@ -64,12 +43,12 @@ typedef struct ip6_fib_table_instance_t_
uword *non_empty_dst_address_length_bitmap;
u8 *prefix_lengths_in_search_order;
i32 dst_address_length_refcounts[129];
-} ip6_fib_table_instance_t;
+} ip6_fib_fwding_table_instance_t;
/**
* The two FIB tables; fwding and non-fwding
*/
-extern ip6_fib_table_instance_t ip6_fib_table[IP6_FIB_NUM_TABLES];
+extern ip6_fib_fwding_table_instance_t ip6_fib_fwding_table;
extern fib_node_index_t ip6_fib_table_lookup(u32 fib_index,
const ip6_address_t *addr,
@@ -115,13 +94,13 @@ always_inline u32
ip6_fib_table_fwding_lookup (u32 fib_index,
const ip6_address_t * dst)
{
- ip6_fib_table_instance_t *table;
+ ip6_fib_fwding_table_instance_t *table;
clib_bihash_kv_24_8_t kv, value;
int i, len;
int rv;
u64 fib;
- table = &ip6_fib_table[IP6_FIB_TABLE_FWDING];
+ table = &ip6_fib_fwding_table;
len = vec_len (table->prefix_lengths_in_search_order);
kv.key[0] = dst->as_u64[0];
@@ -230,6 +209,7 @@ u32 ip6_fib_index_from_table_id (u32 table_id)
}
extern u32 ip6_fib_table_get_index_for_sw_if_index(u32 sw_if_index);
+extern void ip6_fib_table_show (vlib_main_t *vm, fib_table_t *fib_table, int summary);
#endif
diff --git a/src/vnet/interface/runtime.c b/src/vnet/interface/runtime.c
index a88a23bd4c9..6428d5ab75b 100644
--- a/src/vnet/interface/runtime.c
+++ b/src/vnet/interface/runtime.c
@@ -72,6 +72,9 @@ vnet_hw_if_update_runtime_data (vnet_main_t *vnm, u32 hw_if_index)
clib_bitmap_t *pending_int = 0;
int last_int = -1;
+ if (node_index == 0)
+ return;
+
log_debug ("update node '%U' triggered by interface %v",
format_vlib_node_name, vm, node_index, hi->name);
diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c
index c727e519138..69bf4b72ba4 100644
--- a/src/vnet/interface_api.c
+++ b/src/vnet/interface_api.c
@@ -579,7 +579,7 @@ ip_table_bind (fib_protocol_t fproto, u32 sw_if_index, u32 table_id)
fib_index = fib_table_find (fproto, table_id);
mfib_index = mfib_table_find (fproto, table_id);
- if (~0 == fib_index || ~0 == mfib_index)
+ if (~0 == fib_index)
{
return (VNET_API_ERROR_NO_SUCH_FIB);
}
@@ -601,7 +601,8 @@ ip_table_bind (fib_protocol_t fproto, u32 sw_if_index, u32 table_id)
/* clang-format on */
fib_table_bind (fproto, sw_if_index, fib_index);
- mfib_table_bind (fproto, sw_if_index, mfib_index);
+ if (mfib_index != ~0)
+ mfib_table_bind (fproto, sw_if_index, mfib_index);
return (0);
}
diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c
index d340037a15d..614b78489cd 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.c
+++ b/src/vnet/ip-neighbor/ip_neighbor.c
@@ -460,6 +460,7 @@ ip_neighbor_destroy (ip_neighbor_t * ipn)
af = ip_neighbor_get_af (ipn);
IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
+ vlib_time_now (vlib_get_main ()),
ip_neighbor_get_index (ipn));
ip_neighbor_publish (ip_neighbor_get_index (ipn),
@@ -944,20 +945,20 @@ ip_neighbor_show_sorted_i (vlib_main_t * vm,
vlib_cli_command_t * cmd, ip_address_family_t af)
{
ip_neighbor_elt_t *elt, *head;
+ f64 now;
head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
+ now = vlib_time_now (vm);
-
- vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
- "Flags", "Ethernet", "Interface");
+ vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Age", "IP", "Flags",
+ "Ethernet", "Interface");
/* the list is time sorted, newest first, so start from the back
* and work forwards. Stop when we get to one that is alive */
- clib_llist_foreach_reverse(ip_neighbor_elt_pool,
- ipne_anchor, head, elt,
- ({
- vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
- }));
+ clib_llist_foreach_reverse (ip_neighbor_elt_pool, ipne_anchor, head, elt, ({
+ vlib_cli_output (vm, "%U", format_ip_neighbor,
+ now, elt->ipne_index);
+ }));
return (NULL);
}
@@ -969,6 +970,7 @@ ip_neighbor_show_i (vlib_main_t * vm,
{
index_t *ipni, *ipnis = NULL;
u32 sw_if_index;
+ f64 now;
/* Filter entries by interface if given. */
sw_if_index = ~0;
@@ -976,14 +978,15 @@ ip_neighbor_show_i (vlib_main_t * vm,
&sw_if_index);
ipnis = ip_neighbor_entries (sw_if_index, af);
+ now = vlib_time_now (vm);
if (ipnis)
- vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
- "Flags", "Ethernet", "Interface");
+ vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Age", "IP", "Flags",
+ "Ethernet", "Interface");
vec_foreach (ipni, ipnis)
{
- vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
+ vlib_cli_output (vm, "%U", format_ip_neighbor, now, *ipni);
}
vec_free (ipnis);
@@ -1573,13 +1576,12 @@ ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
if (ttl > ipndb_age)
{
- IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
- format_ip_neighbor, ipni, now,
- ipn->ipn_time_last_updated, ipndb_age);
+ IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d", format_ip_neighbor, now, ipni,
+ now, ipn->ipn_time_last_updated, ipndb_age);
if (ipn->ipn_n_probes > 2)
{
/* 3 strikes and yea-re out */
- IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
+ IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, now, ipni);
*wait = 1;
return (IP_NEIGHBOR_AGE_DEAD);
}
diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.c b/src/vnet/ip-neighbor/ip_neighbor_types.c
index 39039a48249..a6f3c26d42f 100644
--- a/src/vnet/ip-neighbor/ip_neighbor_types.c
+++ b/src/vnet/ip-neighbor/ip_neighbor_types.c
@@ -68,19 +68,18 @@ format_ip_neighbor_watcher (u8 * s, va_list * va)
u8 *
format_ip_neighbor (u8 * s, va_list * va)
{
+ f64 now = va_arg (*va, f64);
index_t ipni = va_arg (*va, index_t);
ip_neighbor_t *ipn;
ipn = ip_neighbor_get (ipni);
- return (format (s, "%=12U%=40U%=6U%=20U%U",
- format_vlib_time, vlib_get_main (),
- ipn->ipn_time_last_updated,
- format_ip_address, &ipn->ipn_key->ipnk_ip,
- format_ip_neighbor_flags, ipn->ipn_flags,
- format_mac_address_t, &ipn->ipn_mac,
- format_vnet_sw_if_index_name, vnet_get_main (),
- ipn->ipn_key->ipnk_sw_if_index));
+ return (
+ format (s, "%=12U%=40U%=6U%=20U%U", format_vlib_time, vlib_get_main (),
+ now - ipn->ipn_time_last_updated, format_ip_address,
+ &ipn->ipn_key->ipnk_ip, format_ip_neighbor_flags, ipn->ipn_flags,
+ format_mac_address_t, &ipn->ipn_mac, format_vnet_sw_if_index_name,
+ vnet_get_main (), ipn->ipn_key->ipnk_sw_if_index));
}
static void
diff --git a/src/vnet/ip/icmp6.c b/src/vnet/ip/icmp6.c
index b095f679cc8..f93ebce4bf1 100644
--- a/src/vnet/ip/icmp6.c
+++ b/src/vnet/ip/icmp6.c
@@ -338,7 +338,7 @@ ip6_icmp_error (vlib_main_t * vm,
if (throttle_check (&icmp_throttle, thread_index, r0, seed))
{
- vlib_error_count (vm, node->node_index, ICMP4_ERROR_DROP, 1);
+ vlib_error_count (vm, node->node_index, ICMP6_ERROR_DROP, 1);
from += 1;
n_left_from -= 1;
continue;
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 967f56cf917..fc7d7582dec 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -57,6 +57,23 @@ autoreply define ip_table_add_del
vl_api_ip_table_t table;
};
+/** \brief Add / del table request - version 2
+ A table can be added multiple times, but need be deleted only once.
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param table - the FIB table to add or del
+ @param create_mfib - whether to create mfib or not
+ @param is_add - add or del
+*/
+autoreply define ip_table_add_del_v2
+{
+ u32 client_index;
+ u32 context;
+ vl_api_ip_table_t table;
+ bool create_mfib [default=true];
+ bool is_add [default=true];
+};
+
/** \brief Allocate an unused table
A table can be added multiple times.
If a large number of tables are in use (millions), this API might
diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h
index 9ebefa0cf5d..084243dccfa 100644
--- a/src/vnet/ip/ip.h
+++ b/src/vnet/ip/ip.h
@@ -262,7 +262,7 @@ extern vlib_node_registration_t ip4_inacl_node;
extern vlib_node_registration_t ip6_inacl_node;
void ip_table_create (fib_protocol_t fproto, u32 table_id, u8 is_api,
- const u8 * name);
+ u8 create_mfib, const u8 *name);
void ip_table_delete (fib_protocol_t fproto, u32 table_id, u8 is_api);
diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h
index 56eec523d5b..f8462a5cbff 100644
--- a/src/vnet/ip/ip6.h
+++ b/src/vnet/ip/ip6.h
@@ -68,6 +68,11 @@ typedef struct
/* Index into FIB vector. */
u32 index;
+
+ /**
+ * The hash table DB
+ */
+ uword *fib_entry_by_dst_address;
} ip6_fib_t;
typedef struct ip6_mfib_t
diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c
index 48fb633fd32..31adc90ecab 100644
--- a/src/vnet/ip/ip6_forward.c
+++ b/src/vnet/ip/ip6_forward.c
@@ -51,6 +51,7 @@
#include <vnet/dpo/receive_dpo.h>
#include <vnet/dpo/classify_dpo.h>
#include <vnet/classify/vnet_classify.h>
+#include <vnet/adj/adj_dp.h>
#include <vnet/pg/pg.h>
#ifndef CLIB_MARCH_VARIANT
@@ -1897,18 +1898,6 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
vnet_buffer (p0)->ip.save_rewrite_length = rw_len0;
vnet_buffer (p1)->ip.save_rewrite_length = rw_len1;
- if (do_counters)
- {
- vlib_increment_combined_counter
- (&adjacency_counters,
- thread_index, adj_index0, 1,
- vlib_buffer_length_in_chain (vm, p0) + rw_len0);
- vlib_increment_combined_counter
- (&adjacency_counters,
- thread_index, adj_index1, 1,
- vlib_buffer_length_in_chain (vm, p1) + rw_len1);
- }
-
/* Check MTU of outgoing interface. */
u16 ip0_len =
clib_net_to_host_u16 (ip0->payload_length) +
@@ -1933,16 +1922,15 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
* wants to see the IP header */
if (PREDICT_TRUE (error0 == IP6_ERROR_NONE))
{
- p0->current_data -= rw_len0;
- p0->current_length += rw_len0;
+ vlib_buffer_advance (p0, -(word) rw_len0);
tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
next0 = adj0[0].rewrite_header.next_index;
if (PREDICT_FALSE
(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
- vnet_feature_arc_start_w_cfg_index
- (lm->output_feature_arc_index, tx_sw_if_index0, &next0, p0,
- adj0->ia_cfg_index);
+ vnet_feature_arc_start_w_cfg_index (
+ lm->output_feature_arc_index, tx_sw_if_index0, &next0, p0,
+ adj0->ia_cfg_index);
}
else
{
@@ -1950,18 +1938,16 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
}
if (PREDICT_TRUE (error1 == IP6_ERROR_NONE))
{
- p1->current_data -= rw_len1;
- p1->current_length += rw_len1;
-
+ vlib_buffer_advance (p1, -(word) rw_len1);
tx_sw_if_index1 = adj1[0].rewrite_header.sw_if_index;
vnet_buffer (p1)->sw_if_index[VLIB_TX] = tx_sw_if_index1;
next1 = adj1[0].rewrite_header.next_index;
if (PREDICT_FALSE
(adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
- vnet_feature_arc_start_w_cfg_index
- (lm->output_feature_arc_index, tx_sw_if_index1, &next1, p1,
- adj1->ia_cfg_index);
+ vnet_feature_arc_start_w_cfg_index (
+ lm->output_feature_arc_index, tx_sw_if_index1, &next1, p1,
+ adj1->ia_cfg_index);
}
else
{
@@ -1969,40 +1955,46 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
}
if (is_midchain)
- {
- /* Guess we are only writing on ipv6 header. */
- vnet_rewrite_two_headers (adj0[0], adj1[0],
- ip0, ip1, sizeof (ip6_header_t));
- }
+ /* Guess we are only writing on ipv6 header. */
+ vnet_rewrite_two_headers (adj0[0], adj1[0], ip0, ip1,
+ sizeof (ip6_header_t));
else
/* Guess we are only writing on simple Ethernet header. */
vnet_rewrite_two_headers (adj0[0], adj1[0],
ip0, ip1, sizeof (ethernet_header_t));
+ if (do_counters)
+ {
+ if (error0 == IP6_ERROR_NONE)
+ vlib_increment_combined_counter (
+ &adjacency_counters, thread_index, adj_index0, 1,
+ vlib_buffer_length_in_chain (vm, p0) + rw_len0);
+ if (error1 == IP6_ERROR_NONE)
+ vlib_increment_combined_counter (
+ &adjacency_counters, thread_index, adj_index1, 1,
+ vlib_buffer_length_in_chain (vm, p1) + rw_len1);
+ }
+
if (is_midchain)
{
- if (adj0->sub_type.midchain.fixup_func)
- adj0->sub_type.midchain.fixup_func
- (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
- if (adj1->sub_type.midchain.fixup_func)
- adj1->sub_type.midchain.fixup_func
- (vm, adj1, p1, adj1->sub_type.midchain.fixup_data);
+ if (error0 == IP6_ERROR_NONE)
+ adj_midchain_fixup (vm, adj0, p0, VNET_LINK_IP6);
+ if (error1 == IP6_ERROR_NONE)
+ adj_midchain_fixup (vm, adj1, p1, VNET_LINK_IP6);
}
if (is_mcast)
{
/*
* copy bytes from the IP address into the MAC rewrite
*/
- vnet_ip_mcast_fixup_header (IP6_MCAST_ADDR_MASK,
- adj0->
- rewrite_header.dst_mcast_offset,
- &ip0->dst_address.as_u32[3],
- (u8 *) ip0);
- vnet_ip_mcast_fixup_header (IP6_MCAST_ADDR_MASK,
- adj1->
- rewrite_header.dst_mcast_offset,
- &ip1->dst_address.as_u32[3],
- (u8 *) ip1);
+ if (error0 == IP6_ERROR_NONE)
+ vnet_ip_mcast_fixup_header (
+ IP6_MCAST_ADDR_MASK, adj0->rewrite_header.dst_mcast_offset,
+ &ip0->dst_address.as_u32[3], (u8 *) ip0);
+ if (error1 == IP6_ERROR_NONE)
+ vnet_ip_mcast_fixup_header (
+ IP6_MCAST_ADDR_MASK, adj1->rewrite_header.dst_mcast_offset,
+ &ip1->dst_address.as_u32[3], (u8 *) ip1);
}
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -2061,28 +2053,10 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
}
}
- if (is_midchain)
- {
- /* Guess we are only writing on ip6 header. */
- vnet_rewrite_one_header (adj0[0], ip0, sizeof (ip6_header_t));
- }
- else
- /* Guess we are only writing on simple Ethernet header. */
- vnet_rewrite_one_header (adj0[0], ip0,
- sizeof (ethernet_header_t));
-
/* Update packet buffer attributes/set output interface. */
rw_len0 = adj0[0].rewrite_header.data_bytes;
vnet_buffer (p0)->ip.save_rewrite_length = rw_len0;
- if (do_counters)
- {
- vlib_increment_combined_counter
- (&adjacency_counters,
- thread_index, adj_index0, 1,
- vlib_buffer_length_in_chain (vm, p0) + rw_len0);
- }
-
/* Check MTU of outgoing interface. */
u16 ip0_len =
clib_net_to_host_u16 (ip0->payload_length) +
@@ -2098,9 +2072,7 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
* wants to see the IP header */
if (PREDICT_TRUE (error0 == IP6_ERROR_NONE))
{
- p0->current_data -= rw_len0;
- p0->current_length += rw_len0;
-
+ vlib_buffer_advance (p0, -(word) rw_len0);
tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
@@ -2108,30 +2080,37 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
if (PREDICT_FALSE
(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
- vnet_feature_arc_start_w_cfg_index
- (lm->output_feature_arc_index, tx_sw_if_index0, &next0, p0,
- adj0->ia_cfg_index);
+ vnet_feature_arc_start_w_cfg_index (
+ lm->output_feature_arc_index, tx_sw_if_index0, &next0, p0,
+ adj0->ia_cfg_index);
+
+ if (is_midchain)
+ /* Guess we are only writing on ip6 header. */
+ vnet_rewrite_one_header (adj0[0], ip0, sizeof (ip6_header_t));
+ else
+ /* Guess we are only writing on simple Ethernet header. */
+ vnet_rewrite_one_header (adj0[0], ip0,
+ sizeof (ethernet_header_t));
+
+ if (do_counters)
+ {
+ vlib_increment_combined_counter (
+ &adjacency_counters, thread_index, adj_index0, 1,
+ vlib_buffer_length_in_chain (vm, p0) + rw_len0);
+ }
+
+ if (is_midchain && adj0->sub_type.midchain.fixup_func)
+ adj_midchain_fixup (vm, adj0, p0, VNET_LINK_IP6);
+ if (is_mcast)
+ vnet_ip_mcast_fixup_header (
+ IP6_MCAST_ADDR_MASK, adj0->rewrite_header.dst_mcast_offset,
+ &ip0->dst_address.as_u32[3], (u8 *) ip0);
}
else
{
p0->error = error_node->errors[error0];
}
- if (is_midchain)
- {
- if (adj0->sub_type.midchain.fixup_func)
- adj0->sub_type.midchain.fixup_func
- (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
- }
- if (is_mcast)
- {
- vnet_ip_mcast_fixup_header (IP6_MCAST_ADDR_MASK,
- adj0->
- rewrite_header.dst_mcast_offset,
- &ip0->dst_address.as_u32[3],
- (u8 *) ip0);
- }
-
from += 1;
n_left_from -= 1;
to_next += 1;
@@ -2215,7 +2194,7 @@ VLIB_NODE_FN (ip6_mcast_midchain_node) (vlib_main_t * vm,
VLIB_REGISTER_NODE (ip6_midchain_node) = {
.name = "ip6-midchain",
.vector_size = sizeof (u32),
- .format_trace = format_ip6_forward_next_trace,
+ .format_trace = format_ip6_rewrite_trace,
.sibling_of = "ip6-rewrite",
};
diff --git a/src/vnet/ip/ip6_input.h b/src/vnet/ip/ip6_input.h
index 49e37ec1808..25eae62723d 100644
--- a/src/vnet/ip/ip6_input.h
+++ b/src/vnet/ip/ip6_input.h
@@ -53,11 +53,9 @@ typedef enum
} ip6_input_next_t;
always_inline void
-ip6_input_check_x2 (vlib_main_t * vm,
- vlib_node_runtime_t * error_node,
- vlib_buffer_t * p0, vlib_buffer_t * p1,
- ip6_header_t * ip0, ip6_header_t * ip1,
- u32 * next0, u32 * next1)
+ip6_input_check_x2 (vlib_main_t *vm, vlib_node_runtime_t *error_node,
+ vlib_buffer_t *p0, vlib_buffer_t *p1, ip6_header_t *ip0,
+ ip6_header_t *ip1, u32 *next0, u32 *next1)
{
u8 error0, error1;
@@ -65,13 +63,15 @@ ip6_input_check_x2 (vlib_main_t * vm,
/* Version != 6? Drop it. */
error0 =
- (clib_net_to_host_u32
- (ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
- 6 ? IP6_ERROR_VERSION : error0;
+ (clib_net_to_host_u32 (ip0->ip_version_traffic_class_and_flow_label) >>
+ 28) != 6 ?
+ IP6_ERROR_VERSION :
+ error0;
error1 =
- (clib_net_to_host_u32
- (ip1->ip_version_traffic_class_and_flow_label) >> 28) !=
- 6 ? IP6_ERROR_VERSION : error1;
+ (clib_net_to_host_u32 (ip1->ip_version_traffic_class_and_flow_label) >>
+ 28) != 6 ?
+ IP6_ERROR_VERSION :
+ error1;
/* hop limit < 1? Drop it. for link-local broadcast packets,
* like dhcpv6 packets from client has hop-limit 1, which should not
@@ -81,18 +81,18 @@ ip6_input_check_x2 (vlib_main_t * vm,
error1 = ip1->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error1;
/* L2 length must be at least minimal IP header. */
- error0 =
- p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
- error1 =
- p1->current_length < sizeof (ip1[0]) ? IP6_ERROR_TOO_SHORT : error1;
+ error0 = p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
+ error1 = p1->current_length < sizeof (ip1[0]) ? IP6_ERROR_TOO_SHORT : error1;
if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
{
+ p0->error = error_node->errors[error0];
+
if (error0 == IP6_ERROR_TIME_EXPIRED)
{
- icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
- ICMP6_time_exceeded_ttl_exceeded_in_transit,
- 0);
+ icmp6_error_set_vnet_buffer (
+ p0, ICMP6_time_exceeded,
+ ICMP6_time_exceeded_ttl_exceeded_in_transit, 0);
*next0 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
@@ -102,11 +102,13 @@ ip6_input_check_x2 (vlib_main_t * vm,
}
if (PREDICT_FALSE (error1 != IP6_ERROR_NONE))
{
+ p1->error = error_node->errors[error1];
+
if (error1 == IP6_ERROR_TIME_EXPIRED)
{
- icmp6_error_set_vnet_buffer (p1, ICMP6_time_exceeded,
- ICMP6_time_exceeded_ttl_exceeded_in_transit,
- 0);
+ icmp6_error_set_vnet_buffer (
+ p1, ICMP6_time_exceeded,
+ ICMP6_time_exceeded_ttl_exceeded_in_transit, 0);
*next1 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
@@ -117,9 +119,8 @@ ip6_input_check_x2 (vlib_main_t * vm,
}
always_inline void
-ip6_input_check_x1 (vlib_main_t * vm,
- vlib_node_runtime_t * error_node,
- vlib_buffer_t * p0, ip6_header_t * ip0, u32 * next0)
+ip6_input_check_x1 (vlib_main_t *vm, vlib_node_runtime_t *error_node,
+ vlib_buffer_t *p0, ip6_header_t *ip0, u32 *next0)
{
u8 error0;
@@ -127,9 +128,10 @@ ip6_input_check_x1 (vlib_main_t * vm,
/* Version != 6? Drop it. */
error0 =
- (clib_net_to_host_u32
- (ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
- 6 ? IP6_ERROR_VERSION : error0;
+ (clib_net_to_host_u32 (ip0->ip_version_traffic_class_and_flow_label) >>
+ 28) != 6 ?
+ IP6_ERROR_VERSION :
+ error0;
/* hop limit < 1? Drop it. for link-local broadcast packets,
* like dhcpv6 packets from client has hop-limit 1, which should not
@@ -138,16 +140,16 @@ ip6_input_check_x1 (vlib_main_t * vm,
error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
/* L2 length must be at least minimal IP header. */
- error0 =
- p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
+ error0 = p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
{
+ p0->error = error_node->errors[error0];
if (error0 == IP6_ERROR_TIME_EXPIRED)
{
- icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
- ICMP6_time_exceeded_ttl_exceeded_in_transit,
- 0);
+ icmp6_error_set_vnet_buffer (
+ p0, ICMP6_time_exceeded,
+ ICMP6_time_exceeded_ttl_exceeded_in_transit, 0);
*next0 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
diff --git a/src/vnet/ip/ip6_ll_table.c b/src/vnet/ip/ip6_ll_table.c
index f9172f6c50c..2234ea9df37 100644
--- a/src/vnet/ip/ip6_ll_table.c
+++ b/src/vnet/ip/ip6_ll_table.c
@@ -144,17 +144,20 @@ ip6_ll_table_entry_delete (const ip6_ll_prefix_t * ilp)
fib_node_index_t ip6_ll_entry_index;
u32 fib_index;
+ fib_index = ip6_ll_fib_get (ilp->ilp_sw_if_index);
+ if (~0 == fib_index)
+ return;
+
ip6_ll_entry_index = ip6_ll_table_lookup_exact_match (ilp);
+ if (FIB_NODE_INDEX_INVALID == ip6_ll_entry_index)
+ return;
- if (FIB_NODE_INDEX_INVALID != ip6_ll_entry_index)
- fib_table_entry_delete_index (ip6_ll_entry_index, FIB_SOURCE_IP6_ND);
+ fib_table_entry_delete_index (ip6_ll_entry_index, FIB_SOURCE_IP6_ND);
/*
* if there are no ND sourced prefixes left, then we can clean up this FIB
*/
- fib_index = ip6_ll_fib_get (ilp->ilp_sw_if_index);
- if (~0 != fib_index &&
- 0 == fib_table_get_num_entries (fib_index, FIB_PROTOCOL_IP6,
+ if (0 == fib_table_get_num_entries (fib_index, FIB_PROTOCOL_IP6,
FIB_SOURCE_IP6_ND))
{
fib_table_unlock (fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_IP6_ND);
@@ -208,33 +211,10 @@ ip6_ll_table_show_all (vlib_main_t * vm, u32 fib_index)
vec_free (ctx.entries);
}
-typedef struct
-{
- u32 fib_index;
- u64 count_by_prefix_length[129];
-} count_routes_in_fib_at_prefix_length_arg_t;
-
-static int
-count_routes_in_fib_at_prefix_length (clib_bihash_kv_24_8_t * kvp, void *arg)
-{
- count_routes_in_fib_at_prefix_length_arg_t *ap = arg;
- int mask_width;
-
- if ((kvp->key[2] >> 32) != ap->fib_index)
- return (BIHASH_WALK_CONTINUE);
-
- mask_width = kvp->key[2] & 0xFF;
-
- ap->count_by_prefix_length[mask_width]++;
-
- return (BIHASH_WALK_CONTINUE);
-}
-
static clib_error_t *
ip6_ll_show_fib (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
- count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca;
fib_table_t *fib_table;
int verbose, matching;
ip6_address_t matching_address;
@@ -272,9 +252,6 @@ ip6_ll_show_fib (vlib_main_t * vm,
vec_foreach_index (sw_if_index, ip6_ll_table.ilt_fibs)
{
- fib_source_t source;
- u8 *s = NULL;
-
fib_index = ip6_ll_table.ilt_fibs[sw_if_index];
if (~0 == fib_index)
continue;
@@ -284,44 +261,9 @@ ip6_ll_show_fib (vlib_main_t * vm,
if (!(fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL))
continue;
- s = format (s, "%U, fib_index:%d, locks:[",
- format_fib_table_name, fib_index,
- FIB_PROTOCOL_IP6, fib_index);
- vec_foreach_index (source, fib_table->ft_locks)
- {
- if (0 != fib_table->ft_locks[source])
- {
- s = format (s, "%U:%d, ",
- format_fib_source, source, fib_table->ft_locks[source]);
- }
- }
- s = format (s, "]");
- vlib_cli_output (vm, "%v", s);
- vec_free (s);
-
- /* Show summary? */
+ ip6_fib_table_show (vm, fib_table, !verbose);
if (!verbose)
- {
- clib_bihash_24_8_t *h =
- &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash;
- int len;
-
- vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
-
- clib_memset (ca, 0, sizeof (*ca));
- ca->fib_index = fib_index;
-
- clib_bihash_foreach_key_value_pair_24_8
- (h, count_routes_in_fib_at_prefix_length, ca);
-
- for (len = 128; len >= 0; len--)
- {
- if (ca->count_by_prefix_length[len])
- vlib_cli_output (vm, "%=20d%=16lld",
- len, ca->count_by_prefix_length[len]);
- }
- continue;
- }
+ continue;
if (!matching)
{
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index 644b4988abc..5ced88fec2e 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -636,7 +636,8 @@ vl_api_ip_table_add_del_t_handler (vl_api_ip_table_add_del_t * mp)
if (mp->is_add)
{
- ip_table_create (fproto, table_id, 1, mp->table.name);
+ ip_table_create (fproto, table_id, 1 /* is_api */, 1 /* create_mfib */,
+ mp->table.name);
}
else
{
@@ -647,6 +648,28 @@ vl_api_ip_table_add_del_t_handler (vl_api_ip_table_add_del_t * mp)
}
void
+vl_api_ip_table_add_del_v2_t_handler (vl_api_ip_table_add_del_v2_t *mp)
+{
+ vl_api_ip_table_add_del_v2_reply_t *rmp;
+ fib_protocol_t fproto =
+ (mp->table.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
+ u32 table_id = ntohl (mp->table.table_id);
+ int rv = 0;
+
+ if (mp->is_add)
+ {
+ ip_table_create (fproto, table_id, 1 /* is_api */, mp->create_mfib,
+ mp->table.name);
+ }
+ else
+ {
+ ip_table_delete (fproto, table_id, 1);
+ }
+
+ REPLY_MACRO (VL_API_IP_TABLE_ADD_DEL_V2_REPLY);
+}
+
+void
vl_api_ip_table_allocate_t_handler (vl_api_ip_table_allocate_t *mp)
{
vl_api_ip_table_allocate_reply_t *rmp;
@@ -661,7 +684,8 @@ vl_api_ip_table_allocate_t_handler (vl_api_ip_table_allocate_t *mp)
if (~0 == table_id)
rv = VNET_API_ERROR_EAGAIN;
else
- ip_table_create (fproto, table_id, 1, mp->table.name);
+ ip_table_create (fproto, table_id, 1 /* is_api */, 1 /* create_mfib */,
+ mp->table.name);
REPLY_MACRO2 (VL_API_IP_TABLE_ALLOCATE_REPLY, {
clib_memcpy_fast (&rmp->table, &mp->table, sizeof (mp->table));
@@ -915,8 +939,8 @@ vl_api_ip_route_lookup_v2_t_handler (vl_api_ip_route_lookup_v2_t *mp)
}
void
-ip_table_create (fib_protocol_t fproto,
- u32 table_id, u8 is_api, const u8 * name)
+ip_table_create (fib_protocol_t fproto, u32 table_id, u8 is_api,
+ u8 create_mfib, const u8 *name)
{
u32 fib_index, mfib_index;
vnet_main_t *vnm = vnet_get_main ();
@@ -936,16 +960,23 @@ ip_table_create (fib_protocol_t fproto,
* their own unicast tables.
*/
fib_index = fib_table_find (fproto, table_id);
- mfib_index = mfib_table_find (fproto, table_id);
-
/*
* Always try to re-lock in case the fib was deleted by an API call
* but was not yet freed because some other locks were held
*/
fib_table_find_or_create_and_lock_w_name (
fproto, table_id, (is_api ? FIB_SOURCE_API : FIB_SOURCE_CLI), name);
- mfib_table_find_or_create_and_lock_w_name (
- fproto, table_id, (is_api ? MFIB_SOURCE_API : MFIB_SOURCE_CLI), name);
+
+ if (create_mfib)
+ {
+ /* same for mfib, if needs be */
+ mfib_index = mfib_table_find (fproto, table_id);
+ mfib_table_find_or_create_and_lock_w_name (
+ fproto, table_id, (is_api ? MFIB_SOURCE_API : MFIB_SOURCE_CLI),
+ name);
+ }
+ else
+ mfib_index = 0;
if ((~0 == fib_index) || (~0 == mfib_index))
call_elf_section_ip_table_callbacks (vnm, table_id, 1 /* is_add */ ,
@@ -1655,9 +1686,10 @@ vl_api_ip_table_replace_begin_t_handler (vl_api_ip_table_replace_begin_t * mp)
rv = VNET_API_ERROR_NO_SUCH_FIB;
else
{
+ u32 mfib_index = mfib_table_find (fproto, ntohl (mp->table.table_id));
fib_table_mark (fib_index, fproto, FIB_SOURCE_API);
- mfib_table_mark (mfib_table_find (fproto, ntohl (mp->table.table_id)),
- fproto, MFIB_SOURCE_API);
+ if (mfib_index != INDEX_INVALID)
+ mfib_table_mark (mfib_index, fproto, MFIB_SOURCE_API);
}
REPLY_MACRO (VL_API_IP_TABLE_REPLACE_BEGIN_REPLY);
}
@@ -1677,10 +1709,10 @@ vl_api_ip_table_replace_end_t_handler (vl_api_ip_table_replace_end_t * mp)
rv = VNET_API_ERROR_NO_SUCH_FIB;
else
{
+ u32 mfib_index = mfib_table_find (fproto, ntohl (mp->table.table_id));
fib_table_sweep (fib_index, fproto, FIB_SOURCE_API);
- mfib_table_sweep (mfib_table_find
- (fproto, ntohl (mp->table.table_id)), fproto,
- MFIB_SOURCE_API);
+ if (mfib_index != INDEX_INVALID)
+ mfib_table_sweep (mfib_index, fproto, MFIB_SOURCE_API);
}
REPLY_MACRO (VL_API_IP_TABLE_REPLACE_END_REPLY);
}
@@ -1703,6 +1735,7 @@ vl_api_ip_table_flush_t_handler (vl_api_ip_table_flush_t * mp)
vnet_main_t *vnm = vnet_get_main ();
vnet_interface_main_t *im = &vnm->interface_main;
vnet_sw_interface_t *si;
+ u32 mfib_index;
/* Shut down interfaces in this FIB / clean out intfc routes */
pool_foreach (si, im->sw_interfaces)
@@ -1717,8 +1750,10 @@ vl_api_ip_table_flush_t_handler (vl_api_ip_table_flush_t * mp)
}
fib_table_flush (fib_index, fproto, FIB_SOURCE_API);
- mfib_table_flush (mfib_table_find (fproto, ntohl (mp->table.table_id)),
- fproto, MFIB_SOURCE_API);
+
+ mfib_index = mfib_table_find (fproto, ntohl (mp->table.table_id));
+ if (mfib_index != INDEX_INVALID)
+ mfib_table_flush (mfib_index, fproto, MFIB_SOURCE_API);
}
REPLY_MACRO (VL_API_IP_TABLE_FLUSH_REPLY);
@@ -2128,6 +2163,8 @@ ip_api_hookup (vlib_main_t * vm)
am, REPLY_MSG_ID_BASE + VL_API_IP_ROUTE_ADD_DEL_V2, 1);
vl_api_set_msg_thread_safe (
am, REPLY_MSG_ID_BASE + VL_API_IP_ROUTE_ADD_DEL_V2_REPLY, 1);
+ vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_IP_ADDRESS_DUMP,
+ 1);
return 0;
}
diff --git a/src/vnet/ip/ip_sas.c b/src/vnet/ip/ip_sas.c
index 0fc261724f1..01f6c90baf8 100644
--- a/src/vnet/ip/ip_sas.c
+++ b/src/vnet/ip/ip_sas.c
@@ -54,6 +54,8 @@ ip6_sas_commonlen (const ip6_address_t *a1, const ip6_address_t *a2)
static int
ip4_sas_commonlen (const ip4_address_t *a1, const ip4_address_t *a2)
{
+ if (!a1 || !a2)
+ return 0;
u64 a =
clib_net_to_host_u32 (a1->as_u32) ^ clib_net_to_host_u32 (a2->as_u32);
if (a == 0)
diff --git a/src/vnet/ip/ip_test.c b/src/vnet/ip/ip_test.c
index 727afba67f4..0d1c71063ae 100644
--- a/src/vnet/ip/ip_test.c
+++ b/src/vnet/ip/ip_test.c
@@ -464,6 +464,60 @@ api_ip_table_add_del (vat_main_t *vam)
}
static int
+api_ip_table_add_del_v2 (vat_main_t *vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_ip_table_add_del_v2_t *mp;
+ u8 create_mfib = 1;
+ u32 table_id = ~0;
+ u8 is_ipv6 = 0;
+ u8 is_add = 1;
+ int ret = 0;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "ipv6"))
+ is_ipv6 = 1;
+ else if (unformat (i, "del"))
+ is_add = 0;
+ else if (unformat (i, "add"))
+ is_add = 1;
+ else if (unformat (i, "table %d", &table_id))
+ ;
+ else if (unformat (i, "no-mfib"))
+ create_mfib = 0;
+ else
+ {
+ clib_warning ("parse error '%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ if (~0 == table_id)
+ {
+ errmsg ("missing table-ID");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (IP_TABLE_ADD_DEL_V2, mp);
+
+ mp->table.table_id = ntohl (table_id);
+ mp->table.is_ip6 = is_ipv6;
+ mp->is_add = is_add;
+ mp->create_mfib = create_mfib;
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+
+ return ret;
+}
+
+static int
api_ip_table_replace_begin (vat_main_t *vam)
{
unformat_input_t *i = vam->input;
diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c
index c225c222a38..b978bd79742 100644
--- a/src/vnet/ip/lookup.c
+++ b/src/vnet/ip/lookup.c
@@ -419,10 +419,12 @@ vnet_ip_table_cmd (vlib_main_t * vm,
unformat_input_t _line_input, *line_input = &_line_input;
clib_error_t *error = NULL;
u32 table_id, is_add;
+ u8 create_mfib;
u8 *name = NULL;
is_add = 1;
table_id = ~0;
+ create_mfib = 1;
/* Get a line of input. */
if (!unformat_user (main_input, unformat_line_input, line_input))
@@ -438,6 +440,8 @@ vnet_ip_table_cmd (vlib_main_t * vm,
is_add = 1;
else if (unformat (line_input, "name %s", &name))
;
+ else if (unformat (line_input, "no-mfib"))
+ create_mfib = 0;
else
{
error = unformat_parse_error (line_input);
@@ -459,7 +463,8 @@ vnet_ip_table_cmd (vlib_main_t * vm,
table_id = ip_table_get_unused_id (fproto);
vlib_cli_output (vm, "%u\n", table_id);
}
- ip_table_create (fproto, table_id, 0, name);
+ ip_table_create (fproto, table_id, 0 /* is_api */, create_mfib,
+ name);
}
else
{
@@ -603,6 +608,8 @@ VLIB_CLI_COMMAND (vlib_cli_show_ip6_command, static) = {
* @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.2 GigabitEthernet2/0/0 weight 3}
* To add a route to a particular FIB table (VRF), use:
* @cliexcmd{ip route add 172.16.24.0/24 table 7 via GigabitEthernet2/0/0}
+ * To add a route to drop the traffic:
+ * @cliexcmd{ip route add 172.16.24.0/24 table 100 via 127.0.0.1 drop}
?*/
VLIB_CLI_COMMAND (ip_route_command, static) = {
.path = "ip route",
@@ -612,7 +619,7 @@ VLIB_CLI_COMMAND (ip_route_command, static) = {
"<value>] [udp-encap <value>] [ip4-lookup-in-table <value>] "
"[ip6-lookup-in-table <value>] [mpls-lookup-in-table <value>] "
"[resolve-via-host] [resolve-via-connected] [rx-ip4|rx-ip6 "
- "<interface>] [out-labels <value value value>]",
+ "<interface>] [out-labels <value value value>] [drop]",
.function = vnet_ip_route_cmd,
.is_mp_safe = 1,
};
diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c
index 918ebf03f67..ec4db0fed57 100644
--- a/src/vnet/ipsec/ah_decrypt.c
+++ b/src/vnet/ipsec/ah_decrypt.c
@@ -500,10 +500,10 @@ ah_decrypt_init (vlib_main_t *vm)
{
ipsec_main_t *im = &ipsec_main;
- im->ah4_dec_fq_index =
- vlib_frame_queue_main_init (ah4_decrypt_node.index, 0);
- im->ah6_dec_fq_index =
- vlib_frame_queue_main_init (ah6_decrypt_node.index, 0);
+ im->ah4_dec_fq_index = vlib_frame_queue_main_init (ah4_decrypt_node.index,
+ im->handoff_queue_size);
+ im->ah6_dec_fq_index = vlib_frame_queue_main_init (ah6_decrypt_node.index,
+ im->handoff_queue_size);
return 0;
}
diff --git a/src/vnet/ipsec/ah_encrypt.c b/src/vnet/ipsec/ah_encrypt.c
index 960327f071d..86694660878 100644
--- a/src/vnet/ipsec/ah_encrypt.c
+++ b/src/vnet/ipsec/ah_encrypt.c
@@ -490,10 +490,10 @@ ah_encrypt_init (vlib_main_t *vm)
{
ipsec_main_t *im = &ipsec_main;
- im->ah4_enc_fq_index =
- vlib_frame_queue_main_init (ah4_encrypt_node.index, 0);
- im->ah6_enc_fq_index =
- vlib_frame_queue_main_init (ah6_encrypt_node.index, 0);
+ im->ah4_enc_fq_index = vlib_frame_queue_main_init (ah4_encrypt_node.index,
+ im->handoff_queue_size);
+ im->ah6_enc_fq_index = vlib_frame_queue_main_init (ah6_encrypt_node.index,
+ im->handoff_queue_size);
return 0;
}
diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c
index 26d8ca1deee..01b2d2971b0 100644
--- a/src/vnet/ipsec/esp_decrypt.c
+++ b/src/vnet/ipsec/esp_decrypt.c
@@ -1675,14 +1675,14 @@ esp_decrypt_init (vlib_main_t *vm)
{
ipsec_main_t *im = &ipsec_main;
- im->esp4_dec_fq_index =
- vlib_frame_queue_main_init (esp4_decrypt_node.index, 0);
- im->esp6_dec_fq_index =
- vlib_frame_queue_main_init (esp6_decrypt_node.index, 0);
- im->esp4_dec_tun_fq_index =
- vlib_frame_queue_main_init (esp4_decrypt_tun_node.index, 0);
- im->esp6_dec_tun_fq_index =
- vlib_frame_queue_main_init (esp6_decrypt_tun_node.index, 0);
+ im->esp4_dec_fq_index = vlib_frame_queue_main_init (esp4_decrypt_node.index,
+ im->handoff_queue_size);
+ im->esp6_dec_fq_index = vlib_frame_queue_main_init (esp6_decrypt_node.index,
+ im->handoff_queue_size);
+ im->esp4_dec_tun_fq_index = vlib_frame_queue_main_init (
+ esp4_decrypt_tun_node.index, im->handoff_queue_size);
+ im->esp6_dec_tun_fq_index = vlib_frame_queue_main_init (
+ esp6_decrypt_tun_node.index, im->handoff_queue_size);
return 0;
}
diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c
index dd47053874c..f6d1ecaed24 100644
--- a/src/vnet/ipsec/esp_encrypt.c
+++ b/src/vnet/ipsec/esp_encrypt.c
@@ -589,6 +589,22 @@ esp_prepare_async_frame (vlib_main_t *vm, ipsec_per_thread_data_t *ptd,
async_next, iv, tag, aad, flag);
}
+/* Per RFC6935 section 5, the UDP checksum must be computed when originating
+ * an IPv6 UDP packet. The default behavior may be overridden when conditions
+ * defined by RFC6936 are satisfied. This implementation does not satisfy all
+ * the conditions so the checksum must be computed.
+ */
+static_always_inline void
+set_ip6_udp_cksum_offload (vlib_buffer_t *b, i16 l3_hdr_offset,
+ i16 l4_hdr_offset)
+{
+ vnet_buffer (b)->l3_hdr_offset = l3_hdr_offset;
+ vnet_buffer (b)->l4_hdr_offset = l4_hdr_offset;
+ vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
+ b->flags |= (VNET_BUFFER_F_IS_IP6 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+ VNET_BUFFER_F_L4_HDR_OFFSET_VALID);
+}
+
always_inline uword
esp_encrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_frame_t *frame, vnet_link_t lt, int is_tun,
@@ -869,6 +885,15 @@ esp_encrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
esp_update_ip4_hdr (ip4, len, /* is_transport */ 0, 0);
}
+ if (ipsec_sa_is_set_UDP_ENCAP (sa0) &&
+ ipsec_sa_is_set_IS_TUNNEL_V6 (sa0))
+ {
+ i16 l3_off = b[0]->current_data - hdr_len;
+ i16 l4_off = l3_off + sizeof (ip6_header_t);
+
+ set_ip6_udp_cksum_offload (b[0], l3_off, l4_off);
+ }
+
dpo = &sa0->dpo;
if (!is_tun)
{
@@ -988,6 +1013,14 @@ esp_encrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
esp_fill_udp_hdr (sa0, udp, udp_len);
}
+ if (udp && (VNET_LINK_IP6 == lt))
+ {
+ i16 l3_off = b[0]->current_data - hdr_len + l2_len;
+ i16 l4_off = l3_off + ip_len;
+
+ set_ip6_udp_cksum_offload (b[0], l3_off, l4_off);
+ }
+
sync_next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
}
@@ -1445,16 +1478,16 @@ esp_encrypt_init (vlib_main_t *vm)
{
ipsec_main_t *im = &ipsec_main;
- im->esp4_enc_fq_index =
- vlib_frame_queue_main_init (esp4_encrypt_node.index, 0);
- im->esp6_enc_fq_index =
- vlib_frame_queue_main_init (esp6_encrypt_node.index, 0);
- im->esp4_enc_tun_fq_index =
- vlib_frame_queue_main_init (esp4_encrypt_tun_node.index, 0);
- im->esp6_enc_tun_fq_index =
- vlib_frame_queue_main_init (esp6_encrypt_tun_node.index, 0);
- im->esp_mpls_enc_tun_fq_index =
- vlib_frame_queue_main_init (esp_mpls_encrypt_tun_node.index, 0);
+ im->esp4_enc_fq_index = vlib_frame_queue_main_init (esp4_encrypt_node.index,
+ im->handoff_queue_size);
+ im->esp6_enc_fq_index = vlib_frame_queue_main_init (esp6_encrypt_node.index,
+ im->handoff_queue_size);
+ im->esp4_enc_tun_fq_index = vlib_frame_queue_main_init (
+ esp4_encrypt_tun_node.index, im->handoff_queue_size);
+ im->esp6_enc_tun_fq_index = vlib_frame_queue_main_init (
+ esp6_encrypt_tun_node.index, im->handoff_queue_size);
+ im->esp_mpls_enc_tun_fq_index = vlib_frame_queue_main_init (
+ esp_mpls_encrypt_tun_node.index, im->handoff_queue_size);
return 0;
}
diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c
index f8c39c327ed..8b43dd23cc8 100644
--- a/src/vnet/ipsec/ipsec.c
+++ b/src/vnet/ipsec/ipsec.c
@@ -663,6 +663,7 @@ ipsec_config (vlib_main_t *vm, unformat_input_t *input)
u32 ipsec_spd_fp_num_buckets;
bool fp_spd_ip4_enabled = false;
bool fp_spd_ip6_enabled = false;
+ u32 handoff_queue_size;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
@@ -758,6 +759,11 @@ ipsec_config (vlib_main_t *vm, unformat_input_t *input)
ipsec_tun_table_init (AF_IP6, table_size, n_buckets);
}
+ else if (unformat (input, "async-handoff-queue-size %d",
+ &handoff_queue_size))
+ {
+ im->handoff_queue_size = handoff_queue_size;
+ }
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h
index 4aa09d7560e..9ab054cf2a9 100644
--- a/src/vnet/ipsec/ipsec.h
+++ b/src/vnet/ipsec/ipsec.h
@@ -248,6 +248,8 @@ typedef struct
u32 esp4_dec_tun_fq_index;
u32 esp6_dec_tun_fq_index;
+ u32 handoff_queue_size;
+
/* Number of buckets for flow cache */
u32 ipsec4_out_spd_hash_num_buckets;
u32 ipsec4_out_spd_flow_cache_entries;
diff --git a/src/vnet/ipsec/ipsec_input.c b/src/vnet/ipsec/ipsec_input.c
index 6ccc0be2622..48f7deadda3 100644
--- a/src/vnet/ipsec/ipsec_input.c
+++ b/src/vnet/ipsec/ipsec_input.c
@@ -274,6 +274,159 @@ ip6_addr_match_range (ip6_address_t * a, ip6_address_t * la,
}
always_inline void
+ipsec_collect_ah_trace (vlib_buffer_t **b, vlib_node_runtime_t *node,
+ vlib_main_t *vm, ip4_header_t *ip0, ah_header_t *ah0,
+ u8 has_space0, ipsec_spd_t *spd0, ipsec_policy_t *p0,
+ u32 pi0)
+{
+ if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
+ PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ipsec_input_trace_t *tr = vlib_add_trace (vm, node, b[0], sizeof (*tr));
+
+ tr->proto = ip0->protocol;
+ tr->sa_id = p0 ? p0->sa_id : ~0;
+ tr->spi = has_space0 ? clib_net_to_host_u32 (ah0->spi) : ~0;
+ tr->seq = has_space0 ? clib_net_to_host_u32 (ah0->seq_no) : ~0;
+ tr->spd = spd0->id;
+ tr->policy_index = pi0;
+ }
+}
+
+always_inline void
+ipsec_ah_packet_process (vlib_main_t *vm, ipsec_main_t *im, ip4_header_t *ip0,
+ ah_header_t *ah0, u32 thread_index, ipsec_spd_t *spd0,
+ vlib_buffer_t **b, vlib_node_runtime_t *node,
+ u64 *ipsec_bypassed, u64 *ipsec_dropped,
+ u64 *ipsec_matched, u64 *ipsec_unprocessed, u16 *next)
+
+{
+ ipsec_policy_t *p0 = NULL;
+ u32 pi0 = ~0;
+ u8 has_space0;
+ /* if flow cache is enabled, first search through flow cache for a
+ * policy match and revert back to linear search on failure */
+ bool search_flow_cache = im->input_flow_cache_flag;
+
+ while (1)
+ {
+ if (search_flow_cache)
+ {
+ p0 = ipsec4_input_spd_find_flow_cache_entry (
+ im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
+ IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT);
+ }
+ else
+ {
+ p0 = ipsec_input_protect_policy_match (
+ spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
+ clib_net_to_host_u32 (ip0->dst_address.as_u32),
+ clib_net_to_host_u32 (ah0->spi));
+ }
+
+ has_space0 = vlib_buffer_has_space (b[0], (clib_address_t) (ah0 + 1) -
+ (clib_address_t) ip0);
+
+ if (PREDICT_TRUE ((p0 != NULL) & (has_space0)))
+ {
+ *ipsec_matched += 1;
+ pi0 = p0 - im->policies;
+ vlib_increment_combined_counter (&ipsec_spd_policy_counters,
+ thread_index, pi0, 1,
+ clib_net_to_host_u16 (ip0->length));
+
+ vnet_buffer (b[0])->ipsec.sad_index = p0->sa_index;
+ next[0] = im->ah4_decrypt_next_index;
+ ipsec_collect_ah_trace (b, node, vm, ip0, ah0, has_space0, spd0, p0,
+ pi0);
+ return;
+ }
+ else
+ {
+ p0 = 0;
+ pi0 = ~0;
+ }
+ if (search_flow_cache)
+ {
+ p0 = ipsec4_input_spd_find_flow_cache_entry (
+ im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
+ IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
+ }
+
+ else
+ {
+ p0 = ipsec_input_policy_match (
+ spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
+ clib_net_to_host_u32 (ip0->dst_address.as_u32),
+ IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
+ }
+
+ if (PREDICT_TRUE ((p0 != NULL)))
+ {
+ *ipsec_bypassed += 1;
+ pi0 = p0 - im->policies;
+ vlib_increment_combined_counter (&ipsec_spd_policy_counters,
+ thread_index, pi0, 1,
+ clib_net_to_host_u16 (ip0->length));
+ ipsec_collect_ah_trace (b, node, vm, ip0, ah0, has_space0, spd0, p0,
+ pi0);
+ return;
+ }
+ else
+ {
+ p0 = 0;
+ pi0 = ~0;
+ };
+
+ if (search_flow_cache)
+ {
+ p0 = ipsec4_input_spd_find_flow_cache_entry (
+ im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
+ IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
+ }
+
+ else
+ {
+ p0 = ipsec_input_policy_match (
+ spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
+ clib_net_to_host_u32 (ip0->dst_address.as_u32),
+ IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
+ }
+
+ if (PREDICT_TRUE ((p0 != NULL)))
+ {
+ *ipsec_dropped += 1;
+ pi0 = p0 - im->policies;
+ vlib_increment_combined_counter (&ipsec_spd_policy_counters,
+ thread_index, pi0, 1,
+ clib_net_to_host_u16 (ip0->length));
+
+ next[0] = IPSEC_INPUT_NEXT_DROP;
+ ipsec_collect_ah_trace (b, node, vm, ip0, ah0, has_space0, spd0, p0,
+ pi0);
+ return;
+ }
+ else
+ {
+ p0 = 0;
+ pi0 = ~0;
+ };
+ /* flow cache search failed, retry with linear search */
+ if (search_flow_cache && p0 == NULL)
+ {
+ search_flow_cache = false;
+ }
+ else if (search_flow_cache == false && p0 == NULL)
+ {
+ /* Drop by default if no match on PROTECT, BYPASS or DISCARD */
+ *ipsec_unprocessed += 1;
+ next[0] = IPSEC_INPUT_NEXT_DROP;
+ return;
+ }
+ }
+}
+
+always_inline void
ipsec_esp_packet_process (vlib_main_t *vm, ipsec_main_t *im, ip4_header_t *ip0,
esp_header_t *esp0, u32 thread_index,
ipsec_spd_t *spd0, vlib_buffer_t **b,
@@ -299,10 +452,11 @@ ipsec_esp_packet_process (vlib_main_t *vm, ipsec_main_t *im, ip4_header_t *ip0,
search_flow_cache = im->input_flow_cache_flag;
udp_or_esp:
- /* SPI ID field in the ESP header MUST NOT be a zero value */
if (esp0->spi == 0)
{
- /* Drop the packet if SPI ID is zero */
+ /* RFC 4303, section 2.1: The SPI value of zero (0 is reserved for
+ * local, implementation-specific use and MUST NOT be sent on the wire.
+ */
*ipsec_unprocessed += 1;
next[0] = IPSEC_INPUT_NEXT_DROP;
return;
@@ -523,15 +677,12 @@ VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm,
while (n_left_from > 0)
{
- u32 next32, pi0;
+ u32 next32;
ip4_header_t *ip0;
esp_header_t *esp0 = NULL;
ah_header_t *ah0;
ip4_ipsec_config_t *c0;
ipsec_spd_t *spd0;
- ipsec_policy_t *p0 = NULL;
- u8 has_space0;
- bool search_flow_cache = false;
if (n_left_from > 2)
{
@@ -552,12 +703,10 @@ VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm,
udp_header_t *udp0 = NULL;
udp0 = (udp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
- /* As per rfc3948 in UDP Encapsulated Header, UDP checksum must be
- * Zero, and receivers must not depen upon UPD checksum.
- * inside ESP header , SPI ID value MUST NOT be a zero value
- * */
-
- if (udp0->checksum == 0)
+ /* RFC5996 Section 2.23 "Port 4500 is reserved for
+ * UDP-encapsulated ESP and IKE."
+ */
+ if (clib_host_to_net_u16 (4500) == udp0->dst_port)
{
esp0 = (esp_header_t *) ((u8 *) udp0 + sizeof (udp_header_t));
@@ -582,140 +731,9 @@ VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm,
{
ah0 = (ah_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
- // if flow cache is enabled, first search through flow cache for a
- // policy match and revert back to linear search on failure
- search_flow_cache = im->input_flow_cache_flag;
-
- ah:
- if (search_flow_cache)
- {
- p0 = ipsec4_input_spd_find_flow_cache_entry (
- im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
- IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT);
- }
-
- else
- {
- p0 = ipsec_input_protect_policy_match (
- spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
- clib_net_to_host_u32 (ip0->dst_address.as_u32),
- clib_net_to_host_u32 (ah0->spi));
- }
-
- has_space0 =
- vlib_buffer_has_space (b[0],
- (clib_address_t) (ah0 + 1) -
- (clib_address_t) ip0);
-
- if (PREDICT_TRUE ((p0 != NULL) & (has_space0)))
- {
- ipsec_matched += 1;
-
- pi0 = p0 - im->policies;
- vlib_increment_combined_counter
- (&ipsec_spd_policy_counters,
- thread_index, pi0, 1, clib_net_to_host_u16 (ip0->length));
-
- vnet_buffer (b[0])->ipsec.sad_index = p0->sa_index;
- next[0] = im->ah4_decrypt_next_index;
- goto trace1;
- }
- else
- {
- p0 = 0;
- pi0 = ~0;
- }
-
- if (search_flow_cache)
- {
- p0 = ipsec4_input_spd_find_flow_cache_entry (
- im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
- IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
- }
-
- else
- {
- p0 = ipsec_input_policy_match (
- spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
- clib_net_to_host_u32 (ip0->dst_address.as_u32),
- IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
- }
-
- if (PREDICT_TRUE ((p0 != NULL)))
- {
- ipsec_bypassed += 1;
-
- pi0 = p0 - im->policies;
- vlib_increment_combined_counter (
- &ipsec_spd_policy_counters, thread_index, pi0, 1,
- clib_net_to_host_u16 (ip0->length));
-
- goto trace1;
- }
- else
- {
- p0 = 0;
- pi0 = ~0;
- };
-
- if (search_flow_cache)
- {
- p0 = ipsec4_input_spd_find_flow_cache_entry (
- im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
- IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
- }
-
- else
- {
- p0 = ipsec_input_policy_match (
- spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
- clib_net_to_host_u32 (ip0->dst_address.as_u32),
- IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
- }
-
- if (PREDICT_TRUE ((p0 != NULL)))
- {
- ipsec_dropped += 1;
-
- pi0 = p0 - im->policies;
- vlib_increment_combined_counter (
- &ipsec_spd_policy_counters, thread_index, pi0, 1,
- clib_net_to_host_u16 (ip0->length));
-
- next[0] = IPSEC_INPUT_NEXT_DROP;
- goto trace1;
- }
- else
- {
- p0 = 0;
- pi0 = ~0;
- };
-
- // flow cache search failed, retry with linear search
- if (search_flow_cache && p0 == NULL)
- {
- search_flow_cache = false;
- goto ah;
- }
-
- /* Drop by default if no match on PROTECT, BYPASS or DISCARD */
- ipsec_unprocessed += 1;
- next[0] = IPSEC_INPUT_NEXT_DROP;
-
- trace1:
- if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
- PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
- {
- ipsec_input_trace_t *tr =
- vlib_add_trace (vm, node, b[0], sizeof (*tr));
-
- tr->proto = ip0->protocol;
- tr->sa_id = p0 ? p0->sa_id : ~0;
- tr->spi = has_space0 ? clib_net_to_host_u32 (ah0->spi) : ~0;
- tr->seq = has_space0 ? clib_net_to_host_u32 (ah0->seq_no) : ~0;
- tr->spd = spd0->id;
- tr->policy_index = pi0;
- }
+ ipsec_ah_packet_process (vm, im, ip0, ah0, thread_index, spd0, b,
+ node, &ipsec_bypassed, &ipsec_dropped,
+ &ipsec_matched, &ipsec_unprocessed, next);
}
else
{
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index 4f73f1eab0f..640d9288a42 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -486,7 +486,7 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
return 0;
}
- if (PREDICT_TRUE (sa->seq >= window_size - 1))
+ if (PREDICT_TRUE (window_size > 0 && sa->seq >= window_size - 1))
{
/*
* the last sequence number VPP received is more than one
diff --git a/src/vnet/ipsec/ipsec_tun_in.c b/src/vnet/ipsec/ipsec_tun_in.c
index c82de3ebaff..3dde084cb24 100644
--- a/src/vnet/ipsec/ipsec_tun_in.c
+++ b/src/vnet/ipsec/ipsec_tun_in.c
@@ -60,8 +60,8 @@ format_ipsec_tun_protect_input_trace (u8 * s, va_list * args)
s = format (s, "IPSec: %U seq %u",
format_ipsec6_tunnel_kv, &t->kv6, t->seq);
else
- s = format (s, "IPSec: %U seq %u sa %d",
- format_ipsec4_tunnel_kv, &t->kv4, t->seq);
+ s =
+ format (s, "IPSec: %U seq %u", format_ipsec4_tunnel_kv, &t->kv4, t->seq);
return s;
}
diff --git a/src/vnet/l2/l2_vtr.h b/src/vnet/l2/l2_vtr.h
index 1cd9209973b..40285873273 100644
--- a/src/vnet/l2/l2_vtr.h
+++ b/src/vnet/l2/l2_vtr.h
@@ -75,15 +75,13 @@ typedef struct
always_inline u32
l2_vtr_process (vlib_buffer_t * b0, vtr_config_t * config)
{
- u64 temp_8;
- u32 temp_4;
u8 *eth;
+ u8 save_macs[12];
eth = vlib_buffer_get_current (b0);
/* copy the 12B dmac and smac to a temporary location */
- temp_8 = *((u64 *) eth);
- temp_4 = *((u32 *) (eth + 8));
+ clib_memcpy_fast (save_macs, eth, sizeof (save_macs));
/* adjust for popped tags */
eth += config->pop_bytes;
@@ -95,7 +93,8 @@ l2_vtr_process (vlib_buffer_t * b0, vtr_config_t * config)
}
/* copy the 2 new tags to the start of the packet */
- *((u64 *) (eth + 12 - 8)) = config->raw_tags;
+ clib_memcpy_fast (eth + 12 - 8, &config->raw_tags,
+ sizeof (config->raw_tags));
/* TODO: set cos bits */
@@ -103,8 +102,7 @@ l2_vtr_process (vlib_buffer_t * b0, vtr_config_t * config)
eth -= config->push_bytes;
/* copy the 12 dmac and smac back to the packet */
- *((u64 *) eth) = temp_8;
- *((u32 *) (eth + 8)) = temp_4;
+ clib_memcpy_fast (eth, save_macs, sizeof (save_macs));
/* Update l2 parameters */
vnet_buffer (b0)->l2.l2_len +=
@@ -124,7 +122,6 @@ l2_vtr_process (vlib_buffer_t * b0, vtr_config_t * config)
return 0;
}
-
/*
* Perform the egress pre-vlan tag rewrite EFP Filter check.
* The post-vlan tag rewrite check is a separate graph node.
diff --git a/src/vnet/llc/llc.c b/src/vnet/llc/llc.c
index 4cbf17d48df..e17eaa6fd25 100644
--- a/src/vnet/llc/llc.c
+++ b/src/vnet/llc/llc.c
@@ -208,7 +208,6 @@ add_protocol (llc_main_t * pm, llc_protocol_t protocol, char *protocol_name)
static clib_error_t *
llc_init (vlib_main_t * vm)
{
- clib_error_t *error;
llc_main_t *pm = &llc_main;
clib_memset (pm, 0, sizeof (pm[0]));
@@ -221,9 +220,6 @@ llc_init (vlib_main_t * vm)
foreach_llc_protocol;
#undef _
- if ((error = vlib_call_init_function (vm, snap_init)))
- return error;
-
return vlib_call_init_function (vm, llc_input_init);
}
diff --git a/src/vnet/llc/node.c b/src/vnet/llc/node.c
index d1ee6948269..dee0e060460 100644
--- a/src/vnet/llc/node.c
+++ b/src/vnet/llc/node.c
@@ -313,10 +313,6 @@ llc_register_input_protocol (vlib_main_t * vm,
clib_error_t *error = vlib_call_init_function (vm, llc_input_init);
if (error)
clib_error_report (error);
- /* Otherwise, osi_input_init will wipe out e.g. the snap init */
- error = vlib_call_init_function (vm, osi_input_init);
- if (error)
- clib_error_report (error);
}
pi = llc_get_protocol_info (lm, protocol);
diff --git a/src/vnet/pg/output.c b/src/vnet/pg/output.c
index 042591a7709..fa1a14cc4af 100644
--- a/src/vnet/pg/output.c
+++ b/src/vnet/pg/output.c
@@ -88,7 +88,13 @@ pg_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
pcap_add_buffer (&pif->pcap_main, vm, bi0, ETHERNET_MAX_PACKET_BYTES);
}
if (pif->pcap_file_name != 0)
- pcap_write (&pif->pcap_main);
+ {
+ pcap_packet_type_t pm_pt = pif->pcap_main.packet_type;
+ pif->pcap_main.packet_type =
+ pg_intf_mode_to_pcap_packet_type (pif->mode);
+ pcap_write (&pif->pcap_main);
+ pif->pcap_main.packet_type = pm_pt;
+ }
if ((pif->pcap_main.flags & PCAP_MAIN_INIT_DONE)
&& pif->pcap_main.n_packets_captured >=
pif->pcap_main.n_packets_to_capture)
diff --git a/src/vnet/pg/pg.h b/src/vnet/pg/pg.h
index 6d5b25ba25a..bede747428c 100644
--- a/src/vnet/pg/pg.h
+++ b/src/vnet/pg/pg.h
@@ -306,6 +306,15 @@ typedef enum pg_interface_mode_t_
PG_MODE_IP6,
} pg_interface_mode_t;
+always_inline pcap_packet_type_t
+pg_intf_mode_to_pcap_packet_type (pg_interface_mode_t mode)
+{
+ if ((mode == PG_MODE_IP4) || (mode == PG_MODE_IP6))
+ return PCAP_PACKET_TYPE_ip;
+ else
+ return PCAP_PACKET_TYPE_ethernet;
+}
+
typedef struct
{
/* TX lock */
diff --git a/src/vnet/session/application.c b/src/vnet/session/application.c
index c66548507e5..7c63ada2774 100644
--- a/src/vnet/session/application.c
+++ b/src/vnet/session/application.c
@@ -968,7 +968,7 @@ application_namespace_cleanup (app_namespace_t *app_ns)
ns_index = app_namespace_index (app_ns);
pool_foreach (app, app_main.app_pool)
if (app->ns_index == ns_index)
- vec_add1 (app_indices, app->ns_index);
+ vec_add1 (app_indices, app->app_index);
vec_foreach (app_index, app_indices)
{
diff --git a/src/vnet/session/application_local.c b/src/vnet/session/application_local.c
index 3cb743d10e0..3ac2ba4cfbc 100644
--- a/src/vnet/session/application_local.c
+++ b/src/vnet/session/application_local.c
@@ -70,6 +70,7 @@ typedef struct ct_main_
u32 **fwrk_pending_connects; /**< First wrk pending half-opens */
u32 fwrk_thread; /**< First worker thread */
u8 fwrk_have_flush; /**< Flag for connect flush rpc */
+ u8 is_init;
} ct_main_t;
static ct_main_t ct_main;
@@ -1350,14 +1351,22 @@ ct_enable_disable (vlib_main_t * vm, u8 is_en)
ct_main_t *cm = &ct_main;
ct_worker_t *wrk;
+ if (is_en == 0)
+ return 0;
+
+ if (cm->is_init)
+ return 0;
+
cm->n_workers = vlib_num_workers ();
cm->fwrk_thread = transport_cl_thread ();
vec_validate (cm->wrk, vtm->n_vlib_mains);
+ vec_validate (cm->fwrk_pending_connects, cm->n_workers);
vec_foreach (wrk, cm->wrk)
clib_spinlock_init (&wrk->pending_connects_lock);
clib_spinlock_init (&cm->ho_reuseable_lock);
clib_rwlock_init (&cm->app_segs_lock);
- vec_validate (cm->fwrk_pending_connects, cm->n_workers);
+ cm->is_init = 1;
+
return 0;
}
diff --git a/src/vnet/session/application_namespace.c b/src/vnet/session/application_namespace.c
index f547dcfc031..8b06331d803 100644
--- a/src/vnet/session/application_namespace.c
+++ b/src/vnet/session/application_namespace.c
@@ -34,6 +34,17 @@ static app_namespace_t *app_namespace_pool;
static u8 app_sapi_enabled;
+void
+app_namespace_walk (app_namespace_walk_fn_t fn, void *ctx)
+{
+ app_namespace_t *app_ns;
+
+ pool_foreach (app_ns, app_namespace_pool)
+ {
+ fn (app_ns, ctx);
+ }
+}
+
app_namespace_t *
app_namespace_get (u32 index)
{
@@ -136,12 +147,10 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a)
app_ns->ns_secret = a->secret;
app_ns->sw_if_index = a->sw_if_index;
- app_ns->ip4_fib_index =
- fib_table_find (FIB_PROTOCOL_IP4, a->ip4_fib_id);
- app_ns->ip6_fib_index =
- fib_table_find (FIB_PROTOCOL_IP6, a->ip6_fib_id);
- session_lookup_set_tables_appns (app_ns);
+ app_ns->ip4_fib_index = fib_table_find (FIB_PROTOCOL_IP4, a->ip4_fib_id);
+ app_ns->ip6_fib_index = fib_table_find (FIB_PROTOCOL_IP6, a->ip6_fib_id);
+ session_lookup_set_tables_appns (app_ns);
}
else
{
@@ -164,6 +173,9 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a)
if (app_ns->sock_name)
vec_free (app_ns->sock_name);
+ session_lookup_table_cleanup (FIB_PROTOCOL_IP4, app_ns->ip4_fib_index);
+ session_lookup_table_cleanup (FIB_PROTOCOL_IP6, app_ns->ip6_fib_index);
+
app_namespace_free (app_ns);
}
diff --git a/src/vnet/session/application_namespace.h b/src/vnet/session/application_namespace.h
index 261325cbe0e..b441e3c48f2 100644
--- a/src/vnet/session/application_namespace.h
+++ b/src/vnet/session/application_namespace.h
@@ -15,6 +15,7 @@
#include <vppinfra/socket.h>
#include <vnet/vnet.h>
+#include <vnet/session/session_types.h>
#include <vnet/session/session_table.h>
#ifndef SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_
@@ -87,6 +88,9 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a);
u32 app_namespace_get_fib_index (app_namespace_t * app_ns, u8 fib_proto);
session_table_t *app_namespace_get_local_table (app_namespace_t * app_ns);
+typedef void (*app_namespace_walk_fn_t) (app_namespace_t *app_ns, void *ctx);
+extern void app_namespace_walk (app_namespace_walk_fn_t fn, void *ctx);
+
always_inline app_namespace_t *
app_namespace_get_default (void)
{
diff --git a/src/vnet/session/application_worker.c b/src/vnet/session/application_worker.c
index befdb7c7002..43007ad76ed 100644
--- a/src/vnet/session/application_worker.c
+++ b/src/vnet/session/application_worker.c
@@ -836,9 +836,12 @@ app_worker_mq_wrk_is_congested (app_worker_t *app_wrk, u32 thread_index)
void
app_worker_set_mq_wrk_congested (app_worker_t *app_wrk, u32 thread_index)
{
- clib_atomic_fetch_add_relax (&app_wrk->mq_congested, 1);
ASSERT (thread_index == vlib_get_thread_index ());
- app_wrk->wrk_mq_congested[thread_index] = 1;
+ if (!app_wrk->wrk_mq_congested[thread_index])
+ {
+ clib_atomic_fetch_add_relax (&app_wrk->mq_congested, 1);
+ app_wrk->wrk_mq_congested[thread_index] = 1;
+ }
}
void
diff --git a/src/vnet/session/mma_template.c b/src/vnet/session/mma_template.c
index 4b2770bb756..26a23b2d270 100644
--- a/src/vnet/session/mma_template.c
+++ b/src/vnet/session/mma_template.c
@@ -67,6 +67,9 @@ RT (mma_rule_free) (RTT (mma_rules_table) * srt, RTT (mma_rule) * rule)
void RT (mma_rules_table_free) (RTT (mma_rules_table) * srt)
{
+ RTT (mma_rule) * rule;
+
+ pool_flush (rule, srt->rules, ({}));
pool_free (srt->rules);
}
diff --git a/src/vnet/session/session.api b/src/vnet/session/session.api
index 6affae4112d..26346eaa466 100644
--- a/src/vnet/session/session.api
+++ b/src/vnet/session/session.api
@@ -13,12 +13,19 @@
* limitations under the License.
*/
-option version = "4.0.0";
+option version = "4.0.1";
import "vnet/interface_types.api";
import "vnet/ip/ip_types.api";
+typedef sdl_rule
+{
+ vl_api_prefix_t lcl;
+ u32 action_index;
+ string tag[64];
+};
+
enum transport_proto : u8
{
TRANSPORT_PROTO_API_TCP,
@@ -28,6 +35,14 @@ enum transport_proto : u8
TRANSPORT_PROTO_API_QUIC,
};
+enum rt_backend_engine : u8
+{
+ RT_BACKEND_ENGINE_API_DISABLE = 0,
+ RT_BACKEND_ENGINE_API_RULE_TABLE,
+ RT_BACKEND_ENGINE_API_NONE,
+ RT_BACKEND_ENGINE_API_SDL,
+};
+
/** \brief Application attach to session layer
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@@ -166,11 +181,18 @@ define app_worker_add_del_reply
@param is_enable - disable session layer if 0, enable otherwise
*/
autoreply define session_enable_disable {
+ option deprecated;
u32 client_index;
u32 context;
bool is_enable [default=true];
};
+autoreply define session_enable_disable_v2 {
+ u32 client_index;
+ u32 context;
+ vl_api_rt_backend_engine_t rt_engine_type;
+};
+
/** \brief enable/disable session layer socket api
@param client_index - opaque cookie to identify the sender
client to vpp direction only
@@ -417,6 +439,42 @@ define session_rules_details
string tag[64];
};
+autoreply define session_sdl_add_del {
+ u32 client_index;
+ u32 context;
+ u32 appns_index;
+ bool is_add;
+ u32 count;
+ vl_api_sdl_rule_t r[count];
+};
+
+/** \brief Dump session sdl
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ */
+define session_sdl_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Session sdl details
+ @param context - sender context, to match reply w/ request
+ @param lcl - local prefix
+ @param action_index - the only action defined now is forward to
+ application with index action_index
+ @param appns_index - application namespace where rule is to be applied to
+ @param tag - tag
+ */
+define session_sdl_details
+{
+ u32 context;
+ vl_api_prefix_t lcl;
+ u32 action_index;
+ u32 appns_index;
+ string tag[64];
+};
+
/*
* Local Variables:
* eval: (c-set-style "gnu")
diff --git a/src/vnet/session/session.c b/src/vnet/session/session.c
index 67e7ee39001..ac02281cf5c 100644
--- a/src/vnet/session/session.c
+++ b/src/vnet/session/session.c
@@ -24,6 +24,7 @@
#include <vnet/fib/ip4_fib.h>
#include <vlib/stats/stats.h>
#include <vlib/dma/dma.h>
+#include <vnet/session/session_rules_table.h>
session_main_t session_main;
@@ -104,6 +105,22 @@ session_program_tx_io_evt (session_handle_tu_t sh, session_evt_type_t evt_type)
}
int
+session_program_rx_io_evt (session_handle_tu_t sh)
+{
+ if (sh.thread_index == vlib_get_thread_index ())
+ {
+ session_t *s = session_get_from_handle (sh);
+ return session_enqueue_notify (s);
+ }
+ else
+ {
+ return session_send_evt_to_thread ((void *) &sh.session_index, 0,
+ (u32) sh.thread_index,
+ SESSION_IO_EVT_BUILTIN_RX);
+ }
+}
+
+int
session_send_ctrl_evt_to_thread (session_t * s, session_evt_type_t evt_type)
{
/* only events supported are disconnect, shutdown and reset */
@@ -1659,8 +1676,10 @@ session_transport_close (session_t * s)
{
if (s->session_state == SESSION_STATE_TRANSPORT_CLOSED)
session_set_state (s, SESSION_STATE_CLOSED);
- /* If transport is already deleted, just free the session */
- else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
+ /* If transport is already deleted, just free the session. Half-opens
+ * expected to be already cleaning up at this point */
+ else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED &&
+ !(s->flags & SESSION_F_HALF_OPEN))
session_program_cleanup (s);
return;
}
@@ -1687,7 +1706,8 @@ session_transport_reset (session_t * s)
{
if (s->session_state == SESSION_STATE_TRANSPORT_CLOSED)
session_set_state (s, SESSION_STATE_CLOSED);
- else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
+ else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED &&
+ !(s->flags & SESSION_F_HALF_OPEN))
session_program_cleanup (s);
return;
}
@@ -1866,7 +1886,8 @@ session_register_update_time_fn (session_update_time_fn fn, u8 is_add)
}
else
{
- vec_del1 (smm->update_time_fns, fi_pos);
+ if (found)
+ vec_del1 (smm->update_time_fns, fi_pos);
}
}
@@ -1976,7 +1997,8 @@ session_stats_collector_init (void)
}
static clib_error_t *
-session_manager_main_enable (vlib_main_t * vm)
+session_manager_main_enable (vlib_main_t *vm,
+ session_rt_engine_type_t rt_engine_type)
{
session_main_t *smm = &session_main;
vlib_thread_main_t *vtm = vlib_get_thread_main ();
@@ -1984,6 +2006,9 @@ session_manager_main_enable (vlib_main_t * vm)
session_worker_t *wrk;
int i;
+ if (session_rt_backend_enable_disable (rt_engine_type))
+ return clib_error_return (0, "error on enable backend engine");
+
/* We only initialize once and do not de-initialized on disable */
if (smm->is_initialized)
goto done;
@@ -2062,9 +2087,11 @@ done:
}
static void
-session_manager_main_disable (vlib_main_t * vm)
+session_manager_main_disable (vlib_main_t *vm,
+ session_rt_engine_type_t rt_engine_type)
{
transport_enable_disable (vm, 0 /* is_en */ );
+ session_rt_backend_enable_disable (rt_engine_type);
}
/* in this new callback, cookie hint the index */
@@ -2148,6 +2175,16 @@ session_node_enable_dma (u8 is_en, int n_vlibs)
}
}
+static void
+session_main_start_q_process (vlib_main_t *vm, vlib_node_state_t state)
+{
+ vlib_node_t *n;
+
+ vlib_node_set_state (vm, session_queue_process_node.index, state);
+ n = vlib_get_node (vm, session_queue_process_node.index);
+ vlib_start_process (vm, n->runtime_index);
+}
+
void
session_node_enable_disable (u8 is_en)
{
@@ -2155,7 +2192,6 @@ session_node_enable_disable (u8 is_en)
u8 state = is_en ? VLIB_NODE_STATE_POLLING : VLIB_NODE_STATE_DISABLED;
session_main_t *sm = &session_main;
vlib_main_t *vm;
- vlib_node_t *n;
int n_vlibs, i;
n_vlibs = vlib_get_n_threads ();
@@ -2169,10 +2205,7 @@ session_node_enable_disable (u8 is_en)
if (is_en)
{
session_main_get_worker (0)->state = SESSION_WRK_INTERRUPT;
- vlib_node_set_state (vm, session_queue_process_node.index,
- state);
- n = vlib_get_node (vm, session_queue_process_node.index);
- vlib_start_process (vm, n->runtime_index);
+ session_main_start_q_process (vm, state);
}
else
{
@@ -2195,22 +2228,24 @@ session_node_enable_disable (u8 is_en)
}
clib_error_t *
-vnet_session_enable_disable (vlib_main_t * vm, u8 is_en)
+vnet_session_enable_disable (vlib_main_t *vm,
+ session_enable_disable_args_t *args)
{
clib_error_t *error = 0;
- if (is_en)
+
+ if (args->is_en)
{
if (session_main.is_enabled)
return 0;
- error = session_manager_main_enable (vm);
- session_node_enable_disable (is_en);
+ error = session_manager_main_enable (vm, args->rt_engine_type);
+ session_node_enable_disable (1);
}
else
{
session_main.is_enabled = 0;
- session_manager_main_disable (vm);
- session_node_enable_disable (is_en);
+ session_manager_main_disable (vm, args->rt_engine_type);
+ session_node_enable_disable (0);
}
return error;
@@ -2237,10 +2272,15 @@ static clib_error_t *
session_main_loop_init (vlib_main_t * vm)
{
session_main_t *smm = &session_main;
+
if (smm->session_enable_asap)
{
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ smm->rt_engine_type };
+
vlib_worker_thread_barrier_sync (vm);
- vnet_session_enable_disable (vm, 1 /* is_en */ );
+ vnet_session_enable_disable (vm, &args);
vlib_worker_thread_barrier_release (vm);
}
return 0;
@@ -2330,8 +2370,22 @@ session_config_fn (vlib_main_t * vm, unformat_input_t * input)
smm->port_allocator_min_src_port = tmp;
else if (unformat (input, "max-src-port %d", &tmp))
smm->port_allocator_max_src_port = tmp;
+ else if (unformat (input, "enable rt-backend rule-table"))
+ {
+ smm->rt_engine_type = RT_BACKEND_ENGINE_RULE_TABLE;
+ smm->session_enable_asap = 1;
+ }
+ else if (unformat (input, "enable rt-backend sdl"))
+ {
+ smm->rt_engine_type = RT_BACKEND_ENGINE_SDL;
+ smm->session_enable_asap = 1;
+ }
else if (unformat (input, "enable"))
- smm->session_enable_asap = 1;
+ {
+ /* enable session without rt-backend */
+ smm->rt_engine_type = RT_BACKEND_ENGINE_NONE;
+ smm->session_enable_asap = 1;
+ }
else if (unformat (input, "use-app-socket-api"))
(void) appns_sapi_enable_disable (1 /* is_enable */);
else if (unformat (input, "poll-main"))
diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h
index a5604bf8725..24150fbbcd1 100644
--- a/src/vnet/session/session.h
+++ b/src/vnet/session/session.h
@@ -184,6 +184,19 @@ typedef void (*nat44_original_dst_lookup_fn) (
u16 i2o_dst_port, ip_protocol_t proto, u32 *original_dst,
u16 *original_dst_port);
+#define foreach_rt_engine \
+ _ (DISABLE, "disable") \
+ _ (RULE_TABLE, "enable with rt-backend rule table") \
+ _ (NONE, "enable without rt-backend") \
+ _ (SDL, "enable with rt-backend sdl")
+
+typedef enum
+{
+#define _(v, s) RT_BACKEND_ENGINE_##v,
+ foreach_rt_engine
+#undef _
+} session_rt_engine_type_t;
+
typedef struct session_main_
{
/** Worker contexts */
@@ -235,6 +248,9 @@ typedef struct session_main_
/** Enable session manager at startup */
u8 session_enable_asap;
+ /** Session engine type */
+ session_rt_engine_type_t rt_engine_type;
+
/** Poll session node in main thread */
u8 poll_main;
@@ -292,6 +308,12 @@ typedef enum session_q_process_evt_
SESSION_Q_PROCESS_STOP
} session_q_process_evt_t;
+typedef struct _session_enable_disable_args_t
+{
+ session_rt_engine_type_t rt_engine_type;
+ u8 is_en;
+} session_enable_disable_args_t;
+
#define TRANSPORT_PROTO_INVALID (session_main.last_transport_proto_type + 1)
#define TRANSPORT_N_PROTOS (session_main.last_transport_proto_type + 1)
@@ -461,6 +483,7 @@ int session_send_io_evt_to_thread_custom (void *data, u32 thread_index,
session_evt_type_t evt_type);
int session_program_tx_io_evt (session_handle_tu_t sh,
session_evt_type_t evt_type);
+int session_program_rx_io_evt (session_handle_tu_t sh);
void session_send_rpc_evt_to_thread (u32 thread_index, void *fp,
void *rpc_args);
void session_send_rpc_evt_to_thread_force (u32 thread_index, void *fp,
@@ -811,7 +834,9 @@ session_wrk_update_time (session_worker_t *wrk, f64 now)
void session_wrk_enable_adaptive_mode (session_worker_t *wrk);
fifo_segment_t *session_main_get_wrk_mqs_segment (void);
void session_node_enable_disable (u8 is_en);
-clib_error_t *vnet_session_enable_disable (vlib_main_t * vm, u8 is_en);
+clib_error_t *
+vnet_session_enable_disable (vlib_main_t *vm,
+ session_enable_disable_args_t *args);
void session_wrk_handle_evts_main_rpc (void *);
void session_wrk_program_app_wrk_evts (session_worker_t *wrk,
u32 app_wrk_index);
@@ -920,6 +945,30 @@ pool_program_safe_realloc (void **p, u32 elt_size, u32 align)
} \
while (0)
+always_inline u8
+session_is_enabled_without_rt_backend (void)
+{
+ session_main_t *smm = vnet_get_session_main ();
+
+ return (smm->rt_engine_type == RT_BACKEND_ENGINE_NONE);
+}
+
+always_inline u8
+session_sdl_is_enabled (void)
+{
+ session_main_t *smm = vnet_get_session_main ();
+
+ return (smm->rt_engine_type == RT_BACKEND_ENGINE_SDL);
+}
+
+always_inline u8
+session_rule_table_is_enabled (void)
+{
+ session_main_t *smm = vnet_get_session_main ();
+
+ return (smm->rt_engine_type == RT_BACKEND_ENGINE_RULE_TABLE);
+}
+
#endif /* __included_session_h__ */
/*
diff --git a/src/vnet/session/session_api.c b/src/vnet/session/session_api.c
index 48eb932a2c9..a729baaae29 100644
--- a/src/vnet/session/session_api.c
+++ b/src/vnet/session/session_api.c
@@ -18,9 +18,10 @@
#include <vnet/session/application.h>
#include <vnet/session/application_interface.h>
#include <vnet/session/application_local.h>
-#include <vnet/session/session_rules_table.h>
-#include <vnet/session/session_table.h>
#include <vnet/session/session.h>
+#include <vnet/session/session_table.h>
+#include <vnet/session/session_rules_table.h>
+#include <vnet/session/session_sdl.h>
#include <vnet/ip/ip_types_api.h>
#include <vnet/format_fns.h>
@@ -30,6 +31,105 @@
#define REPLY_MSG_ID_BASE session_main.msg_id_base
#include <vlibapi/api_helper_macros.h>
+VLIB_REGISTER_LOG_CLASS (session_api_log, static) = { .class_name = "session",
+ .subclass_name = "api" };
+
+#define log_debug(fmt, ...) \
+ vlib_log_debug (session_api_log.class, "%s: " fmt, __func__, __VA_ARGS__)
+#define log_warn(fmt, ...) \
+ vlib_log_warn (session_api_log.class, fmt, __VA_ARGS__)
+#define log_err(fmt, ...) \
+ vlib_log_err (session_api_log.class, fmt, __VA_ARGS__)
+
+static int
+verify_message_len (void *mp, u64 expected_len, char *where)
+{
+ u32 supplied_len = vl_msg_api_get_msg_length (mp);
+
+ if (supplied_len < expected_len)
+ {
+ log_err ("%s: Supplied message length %d is less than expected %d",
+ where, supplied_len, expected_len);
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+static void
+vl_api_session_sdl_add_del_t_handler (vl_api_session_sdl_add_del_t *mp)
+{
+ vl_api_session_sdl_add_del_reply_t *rmp;
+ session_rule_add_del_args_t args;
+ session_rule_table_add_del_args_t *table_args = &args.table_args;
+ int rv = 0;
+ u32 count = clib_net_to_host_u32 (mp->count);
+ u64 expected_len = sizeof (*mp) + count * sizeof (mp->r[0]);
+
+ if ((session_main.is_enabled == 0) || (session_sdl_is_enabled () == 0))
+ {
+ rv = VNET_API_ERROR_FEATURE_DISABLED;
+ goto done;
+ }
+
+ if (!verify_message_len (mp, expected_len, "session_sdl_add_del"))
+ {
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ goto done;
+ }
+
+ clib_memset (&args, 0, sizeof (args));
+ table_args->is_add = mp->is_add;
+ args.scope = SESSION_RULE_SCOPE_GLOBAL;
+ args.appns_index = clib_net_to_host_u32 (mp->appns_index);
+ for (int i = 0; i < count; i++)
+ {
+ mp->r[i].tag[sizeof (mp->r[i].tag) - 1] = 0;
+ table_args->tag = format (0, "%s", mp->r[i].tag);
+ ip_prefix_decode (&mp->r[i].lcl, &table_args->lcl);
+ /*
+ * Need to set fp_proto for vnet_session_rule_add_del to find the
+ * correct table
+ */
+ table_args->rmt.fp_proto = table_args->lcl.fp_proto;
+ table_args->action_index = clib_net_to_host_u32 (mp->r[i].action_index);
+
+ rv = vnet_session_rule_add_del (&args);
+ vec_free (table_args->tag);
+ if (rv)
+ {
+ log_err ("session_sdl add del returned on %U @index %d: %U",
+ format_ip46_address, &table_args->lcl.fp_addr,
+ IP46_TYPE_ANY, i, format_session_error, rv);
+
+ /* roll back */
+ table_args->is_add = !mp->is_add;
+ for (int j = i - 1; j >= 0; j--)
+ {
+ mp->r[j].tag[sizeof (mp->r[j].tag) - 1] = 0;
+ table_args->tag = format (0, "%s", mp->r[j].tag);
+ ip_prefix_decode (&mp->r[j].lcl, &table_args->lcl);
+ table_args->rmt.fp_proto = table_args->lcl.fp_proto;
+ table_args->action_index =
+ clib_net_to_host_u32 (mp->r[j].action_index);
+ int rv2 = vnet_session_rule_add_del (&args);
+ vec_free (table_args->tag);
+ if (rv2)
+ log_err ("rollback session_sdl add del returned on %U "
+ "@index %d: %U",
+ format_ip46_address, &table_args->lcl.fp_addr,
+ IP46_TYPE_ANY, j, format_session_error, rv2);
+ }
+ break;
+ }
+ }
+
+done:
+ REPLY_MACRO (VL_API_SESSION_SDL_ADD_DEL_REPLY);
+}
+
static transport_proto_t
api_session_transport_proto_decode (const vl_api_transport_proto_t * api_tp)
{
@@ -520,12 +620,54 @@ vl_api_session_enable_disable_t_handler (vl_api_session_enable_disable_t * mp)
vl_api_session_enable_disable_reply_t *rmp;
vlib_main_t *vm = vlib_get_main ();
int rv = 0;
+ session_enable_disable_args_t args;
- vnet_session_enable_disable (vm, mp->is_enable);
+ args.is_en = mp->is_enable;
+ if (mp->is_enable)
+ args.rt_engine_type = RT_BACKEND_ENGINE_RULE_TABLE;
+ else
+ args.rt_engine_type = RT_BACKEND_ENGINE_DISABLE;
+
+ if (vnet_session_enable_disable (vm, &args))
+ rv = VNET_API_ERROR_INVALID_ARGUMENT;
REPLY_MACRO (VL_API_SESSION_ENABLE_DISABLE_REPLY);
}
static void
+vl_api_session_enable_disable_v2_t_handler (
+ vl_api_session_enable_disable_v2_t *mp)
+{
+ vl_api_session_enable_disable_v2_reply_t *rmp;
+ vlib_main_t *vm = vlib_get_main ();
+ int rv = 0;
+ session_enable_disable_args_t args;
+
+ STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_DISABLE ==
+ RT_BACKEND_ENGINE_DISABLE,
+ "API value mismatch");
+ STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_NONE ==
+ RT_BACKEND_ENGINE_NONE,
+ "API value mismatch");
+ STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_RULE_TABLE ==
+ RT_BACKEND_ENGINE_RULE_TABLE,
+ "API value mismatch");
+ STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_SDL ==
+ RT_BACKEND_ENGINE_SDL,
+ "API value mismatch");
+
+ args.rt_engine_type = (session_rt_engine_type_t) mp->rt_engine_type;
+ if (args.rt_engine_type == RT_BACKEND_ENGINE_DISABLE)
+ args.is_en = 0;
+ else
+ args.is_en = 1;
+
+ if (vnet_session_enable_disable (vm, &args))
+ rv = VNET_API_ERROR_INVALID_VALUE;
+
+ REPLY_MACRO (VL_API_SESSION_ENABLE_DISABLE_V2_REPLY);
+}
+
+static void
vl_api_session_sapi_enable_disable_t_handler (
vl_api_session_sapi_enable_disable_t *mp)
{
@@ -962,6 +1104,12 @@ vl_api_session_rule_add_del_t_handler (vl_api_session_rule_add_del_t * mp)
session_rule_table_add_del_args_t *table_args = &args.table_args;
int rv = 0;
+ if (session_main_is_enabled () == 0)
+ {
+ rv = VNET_API_ERROR_FEATURE_DISABLED;
+ goto done;
+ }
+
clib_memset (&args, 0, sizeof (args));
ip_prefix_decode (&mp->lcl, &table_args->lcl);
@@ -986,6 +1134,7 @@ vl_api_session_rule_add_del_t_handler (vl_api_session_rule_add_del_t * mp)
rv = VNET_API_ERROR_UNSPECIFIED;
}
vec_free (table_args->tag);
+done:
REPLY_MACRO (VL_API_SESSION_RULE_ADD_DEL_REPLY);
}
@@ -1012,6 +1161,8 @@ send_session_rule_details4 (mma_rule_16_t * rule, u8 is_local,
ip_set (&rmt.fp_addr, &match->rmt_ip, 1);
lcl.fp_len = ip4_mask_to_preflen (&mask->lcl_ip);
rmt.fp_len = ip4_mask_to_preflen (&mask->rmt_ip);
+ lcl.fp_proto = FIB_PROTOCOL_IP4;
+ rmt.fp_proto = FIB_PROTOCOL_IP4;
ip_prefix_encode (&lcl, &rmp->lcl);
ip_prefix_encode (&rmt, &rmp->rmt);
@@ -1054,6 +1205,8 @@ send_session_rule_details6 (mma_rule_40_t * rule, u8 is_local,
ip_set (&rmt.fp_addr, &match->rmt_ip, 0);
lcl.fp_len = ip6_mask_to_preflen (&mask->lcl_ip);
rmt.fp_len = ip6_mask_to_preflen (&mask->rmt_ip);
+ lcl.fp_proto = FIB_PROTOCOL_IP6;
+ rmt.fp_proto = FIB_PROTOCOL_IP6;
ip_prefix_encode (&lcl, &rmp->lcl);
ip_prefix_encode (&rmt, &rmp->rmt);
@@ -1120,14 +1273,89 @@ vl_api_session_rules_dump_t_handler (vl_api_session_rules_dump_t * mp)
return;
session_table_foreach (st, ({
- for (tp = 0; tp < TRANSPORT_N_PROTOS; tp++)
- {
- send_session_rules_table_details (&st->session_rules[tp],
- st->active_fib_proto, tp,
- st->is_local, st->appns_index, reg,
- mp->context);
- }
- }));
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
+ for (tp = 0; tp < TRANSPORT_N_PROTOS; tp++)
+ {
+ session_rules_table_t *srt =
+ srtg_handle_to_srt (st->srtg_handle, tp);
+ send_session_rules_table_details (
+ srt, st->active_fib_proto, tp, st->is_local,
+ st->appns_index, reg, mp->context);
+ }
+ }));
+}
+
+typedef struct session_sdl_table_walk_ctx_
+{
+ vl_api_registration_t *reg;
+ u32 mp_context;
+ u32 appns_index;
+} session_sdl_table_walk_ctx;
+
+static void
+send_session_sdl_details (u32 fei, ip46_address_t *lcl_ip, u16 fp_len,
+ u32 action_index, u32 fp_proto, u8 *tag, void *args)
+{
+ session_sdl_table_walk_ctx *ctx = args;
+ vl_api_registration_t *reg = ctx->reg;
+ u32 appns_index = ctx->appns_index;
+ u32 context = ctx->mp_context;
+ vl_api_session_sdl_details_t *rmp = 0;
+ fib_prefix_t lcl;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ clib_memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_DETAILS);
+ rmp->context = context;
+
+ clib_memset (&lcl, 0, sizeof (lcl));
+ if (fp_proto == FIB_PROTOCOL_IP4)
+ ip_set (&lcl.fp_addr, &lcl_ip->ip4, 1);
+ else
+ ip_set (&lcl.fp_addr, &lcl_ip->ip6, 0);
+ lcl.fp_len = fp_len;
+ lcl.fp_proto = fp_proto,
+
+ ip_prefix_encode (&lcl, &rmp->lcl);
+ rmp->action_index = clib_host_to_net_u32 (action_index);
+ rmp->appns_index = clib_host_to_net_u32 (appns_index);
+ if (tag)
+ {
+ clib_memcpy_fast (rmp->tag, tag, vec_len (tag));
+ rmp->tag[vec_len (tag)] = 0;
+ }
+
+ vl_api_send_msg (reg, (u8 *) rmp);
+}
+
+static void
+vl_api_session_sdl_dump_t_handler (vl_api_session_rules_dump_t *mp)
+{
+ vl_api_registration_t *reg;
+ session_table_t *st;
+ session_sdl_table_walk_ctx ctx;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ ctx.reg = reg;
+ ctx.mp_context = mp->context;
+
+ session_table_foreach (
+ st, ({
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
+ {
+ ctx.appns_index = st->appns_index;
+
+ if (st->active_fib_proto == FIB_PROTOCOL_IP4)
+ session_sdl_table_walk4 (st->srtg_handle, send_session_sdl_details,
+ &ctx);
+ else
+ session_sdl_table_walk6 (st->srtg_handle, send_session_sdl_details,
+ &ctx);
+ }
+ }));
}
static void
@@ -1825,11 +2053,21 @@ appns_sapi_add_ns_socket (app_namespace_t * app_ns)
static clib_error_t *
session_api_hookup (vlib_main_t *vm)
{
+ api_main_t *am = vlibapi_get_main ();
+
/*
* Set up the (msg_name, crc, message-id) table
*/
REPLY_MSG_ID_BASE = setup_message_id_table ();
+ vl_api_set_msg_thread_safe (
+ am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_ADD_DEL, 1);
+ vl_api_set_msg_thread_safe (
+ am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_ADD_DEL_REPLY, 1);
+ vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_DUMP,
+ 1);
+ vl_api_set_msg_thread_safe (
+ am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_DETAILS, 1);
return 0;
}
diff --git a/src/vnet/session/session_cli.c b/src/vnet/session/session_cli.c
index 569a77bccc1..c29a465d056 100644
--- a/src/vnet/session/session_cli.c
+++ b/src/vnet/session/session_cli.c
@@ -237,6 +237,27 @@ done:
return rv;
}
+static uword
+unformat_ip_port (unformat_input_t *input, va_list *args)
+{
+ ip46_address_t *ip = va_arg (*args, ip46_address_t *);
+ u16 *port = va_arg (*args, u16 *);
+
+ if (unformat (input, "%U:%d", unformat_ip46_address, ip, IP46_TYPE_ANY,
+ port))
+ ;
+ else if (unformat (input, "%U", unformat_ip46_address, ip, IP46_TYPE_ANY))
+ {
+ *port = 0;
+ }
+ else
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
uword
unformat_session (unformat_input_t * input, va_list * args)
{
@@ -360,30 +381,80 @@ session_cli_show_all_sessions (vlib_main_t * vm, int verbose)
}
}
+typedef enum
+{
+ SESSION_CLI_FILTER_FORCE_PRINT = 1 << 0,
+} session_cli_filter_flags_t;
+
+typedef enum
+{
+ SESSION_CLI_FILTER_ENDPT_LOCAL = 1 << 0,
+ SESSION_CLI_FILTER_ENDPT_REMOTE = 1 << 1,
+} session_cli_endpt_flags_t;
+
+typedef struct session_cli_filter_
+{
+ session_cli_filter_flags_t flags;
+ struct
+ {
+ u32 start;
+ u32 end;
+ } range;
+ transport_endpoint_t endpt;
+ session_cli_endpt_flags_t endpt_flags;
+ session_state_t *states;
+ transport_proto_t transport_proto;
+ u32 thread_index;
+ u32 verbose;
+} session_cli_filter_t;
+
static int
-session_cli_filter_check (session_t * s, session_state_t * states,
- transport_proto_t tp)
+session_cli_filter_check (session_t *s, session_cli_filter_t *sf)
{
- if (states)
+ transport_connection_t *tc;
+
+ if (sf->states)
{
session_state_t *state;
- vec_foreach (state, states) if (s->session_state == *state)
+ vec_foreach (state, sf->states)
+ if (s->session_state == *state)
goto check_transport;
return 0;
}
check_transport:
- if (tp != TRANSPORT_PROTO_INVALID && session_get_transport_proto (s) != tp)
+ if (sf->transport_proto != TRANSPORT_PROTO_INVALID &&
+ session_get_transport_proto (s) != sf->transport_proto)
return 0;
- return 1;
+ if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
+ return 0;
+
+ /* No explicit ip:port match requested */
+ if (!sf->endpt_flags)
+ return 1;
+
+ tc = session_get_transport (s);
+ if (sf->endpt_flags & SESSION_CLI_FILTER_ENDPT_LOCAL)
+ {
+ if (!ip46_address_cmp (&sf->endpt.ip, &tc->lcl_ip) &&
+ (sf->endpt.port == 0 ||
+ sf->endpt.port == clib_net_to_host_u16 (tc->lcl_port)))
+ return 1;
+ }
+ if (sf->endpt_flags & SESSION_CLI_FILTER_ENDPT_REMOTE)
+ {
+ if (!ip46_address_cmp (&sf->endpt.ip, &tc->rmt_ip) &&
+ (sf->endpt.port == 0 ||
+ sf->endpt.port == clib_net_to_host_u16 (tc->rmt_port)))
+ return 1;
+ }
+ return 0;
}
static void
-session_cli_show_session_filter (vlib_main_t * vm, u32 thread_index,
- u32 start, u32 end, session_state_t * states,
- transport_proto_t tp, int verbose)
+session_cli_show_session_filter (vlib_main_t *vm, session_cli_filter_t *sf)
{
u8 output_suppressed = 0;
session_worker_t *wrk;
@@ -391,54 +462,62 @@ session_cli_show_session_filter (vlib_main_t * vm, u32 thread_index,
u32 count = 0, max_index;
int i;
- wrk = session_main_get_worker_if_valid (thread_index);
+ if (sf->range.end < sf->range.start)
+ {
+ vlib_cli_output (vm, "invalid range start: %u end: %u", sf->range.start,
+ sf->range.end);
+ return;
+ }
+
+ wrk = session_main_get_worker_if_valid (sf->thread_index);
if (!wrk)
{
- vlib_cli_output (vm, "invalid thread index %u", thread_index);
+ vlib_cli_output (vm, "invalid thread index %u", sf->thread_index);
return;
}
pool = wrk->sessions;
- if (tp == TRANSPORT_PROTO_INVALID && states == 0 && !verbose
- && (start == 0 && end == ~0))
+ if (sf->transport_proto == TRANSPORT_PROTO_INVALID && sf->states == 0 &&
+ !sf->verbose && (sf->range.start == 0 && sf->range.end == ~0))
{
- vlib_cli_output (vm, "Thread %d: %u sessions", thread_index,
+ vlib_cli_output (vm, "Thread %d: %u sessions", sf->thread_index,
pool_elts (pool));
return;
}
max_index = pool_len (pool) ? pool_len (pool) - 1 : 0;
- for (i = start; i <= clib_min (end, max_index); i++)
+ for (i = sf->range.start; i <= clib_min (sf->range.end, max_index); i++)
{
if (pool_is_free_index (pool, i))
continue;
s = pool_elt_at_index (pool, i);
- if (session_cli_filter_check (s, states, tp))
+ if (!session_cli_filter_check (s, sf))
+ continue;
+
+ count += 1;
+ if (sf->verbose)
{
- count += 1;
- if (verbose)
- {
- if (count > 50 || (verbose > 1 && count > 10))
- {
- output_suppressed = 1;
- continue;
- }
- if (s->session_state < SESSION_STATE_TRANSPORT_DELETED)
- vlib_cli_output (vm, "%U", format_session, s, verbose);
- }
+ if (!(sf->flags & SESSION_CLI_FILTER_FORCE_PRINT) &&
+ (count > 50 || (sf->verbose > 1 && count > 10)))
+ {
+ output_suppressed = 1;
+ continue;
+ }
+ vlib_cli_output (vm, "%U", format_session, s, sf->verbose);
}
}
if (!output_suppressed)
vlib_cli_output (vm, "Thread %d: %u sessions matched filter",
- thread_index, count);
+ sf->thread_index, count);
else
- vlib_cli_output (vm, "Thread %d: %u sessions matched filter. Not all"
- " shown. Use finer grained filter.", thread_index,
- count);
+ vlib_cli_output (vm,
+ "Thread %d: %u sessions matched filter. Not all"
+ " shown. Use finer grained filter.",
+ sf->thread_index, count);
}
void
@@ -482,14 +561,33 @@ session_cli_print_session_states (vlib_main_t * vm)
#undef _
}
+static u8 *
+format_rt_backend (u8 *s, va_list *args)
+{
+ u32 i = va_arg (*args, u32);
+ u8 *t = 0;
+
+ switch (i)
+ {
+#define _(v, s) \
+ case RT_BACKEND_ENGINE_##v: \
+ t = (u8 *) s; \
+ break;
+ foreach_rt_engine
+#undef _
+ default : return format (s, "unknown");
+ }
+ return format (s, "%s", t);
+}
+
static clib_error_t *
show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
{
u8 one_session = 0, do_listeners = 0, sst, do_elog = 0, do_filter = 0;
- u32 track_index, thread_index = 0, start = 0, end = ~0, session_index;
+ u32 track_index, thread_index = 0, session_index;
transport_proto_t transport_proto = TRANSPORT_PROTO_INVALID;
- session_state_t state = SESSION_N_STATES, *states = 0;
+ session_state_t state = SESSION_N_STATES;
session_main_t *smm = &session_main;
clib_error_t *error = 0;
app_worker_t *app_wrk;
@@ -498,18 +596,51 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
u8 do_events = 0;
int verbose = 0;
session_t *s;
+ session_cli_filter_t sf = {
+ .transport_proto = TRANSPORT_PROTO_INVALID,
+ .range = { 0, ~0 },
+ };
session_cli_return_if_not_enabled ();
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
- if (unformat (input, "verbose %d", &verbose))
+ /*
+ * helpers
+ */
+ if (unformat (input, "protos"))
+ {
+ vlib_cli_output (vm, "%U", format_transport_protos);
+ goto done;
+ }
+ else if (unformat (input, "rt-backend"))
+ {
+ vlib_cli_output (vm, "%U", format_rt_backend, smm->rt_engine_type);
+ goto done;
+ }
+ else if (unformat (input, "states"))
+ {
+ session_cli_print_session_states (vm);
+ goto done;
+ }
+ else if (unformat (input, "verbose %d", &verbose))
;
else if (unformat (input, "verbose"))
verbose = 1;
+ /*
+ * listeners
+ */
else if (unformat (input, "listeners %U", unformat_transport_proto,
&transport_proto))
do_listeners = 1;
+ /*
+ * session events
+ */
+ else if (unformat (input, "events"))
+ do_events = 1;
+ /*
+ * single session filter
+ */
else if (unformat (input, "%U", unformat_session, &s))
{
one_session = 1;
@@ -525,17 +656,9 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
}
one_session = 1;
}
- else if (unformat (input, "thread %u", &thread_index))
- {
- do_filter = 1;
- }
- else if (unformat (input, "state %U", unformat_session_state, &state))
- {
- vec_add1 (states, state);
- do_filter = 1;
- }
- else if (unformat (input, "proto %U index %u", unformat_transport_proto,
- &transport_proto, &transport_index))
+ else if (unformat (input, "thread %u proto %U index %u", &thread_index,
+ unformat_transport_proto, &transport_proto,
+ &transport_index))
{
transport_connection_t *tc;
tc = transport_get_connection (transport_proto, transport_index,
@@ -556,30 +679,54 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
}
one_session = 1;
}
+ else if (unformat (input, "elog"))
+ do_elog = 1;
+ /*
+ * session filter
+ */
+ else if (unformat (input, "thread %u", &sf.thread_index))
+ {
+ do_filter = 1;
+ }
+ else if (unformat (input, "state %U", unformat_session_state, &state))
+ {
+ vec_add1 (sf.states, state);
+ do_filter = 1;
+ }
else if (unformat (input, "proto %U", unformat_transport_proto,
- &transport_proto))
+ &sf.transport_proto))
do_filter = 1;
- else if (unformat (input, "range %u %u", &start, &end))
+ else if (unformat (input, "range %u %u", &sf.range.start, &sf.range.end))
do_filter = 1;
- else if (unformat (input, "range %u", &start))
+ else if (unformat (input, "range %u", &sf.range.start))
{
- end = start + 50;
+ sf.range.end = sf.range.start + 50;
do_filter = 1;
}
- else if (unformat (input, "elog"))
- do_elog = 1;
- else if (unformat (input, "protos"))
+ else if (unformat (input, "lcl %U", unformat_ip_port, &sf.endpt.ip,
+ &sf.endpt.port))
{
- vlib_cli_output (vm, "%U", format_transport_protos);
- goto done;
+ sf.endpt_flags |= SESSION_CLI_FILTER_ENDPT_LOCAL;
+ do_filter = 1;
}
- else if (unformat (input, "states"))
+ else if (unformat (input, "rmt %U", unformat_ip_port, &sf.endpt.ip,
+ &sf.endpt.port))
{
- session_cli_print_session_states (vm);
- goto done;
+ sf.endpt_flags |= SESSION_CLI_FILTER_ENDPT_REMOTE;
+ do_filter = 1;
+ }
+ else if (unformat (input, "ep %U", unformat_ip_port, &sf.endpt.ip,
+ &sf.endpt.port))
+ {
+ sf.endpt_flags |=
+ SESSION_CLI_FILTER_ENDPT_REMOTE | SESSION_CLI_FILTER_ENDPT_LOCAL;
+ do_filter = 1;
+ }
+ else if (unformat (input, "force-print"))
+ {
+ sf.flags |= SESSION_CLI_FILTER_FORCE_PRINT;
+ do_filter = 1;
}
- else if (unformat (input, "events"))
- do_events = 1;
else
{
error = clib_error_return (0, "unknown input `%U'",
@@ -636,31 +783,26 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
if (do_filter)
{
- if (end < start)
- {
- error = clib_error_return (0, "invalid range start: %u end: %u",
- start, end);
- goto done;
- }
- session_cli_show_session_filter (vm, thread_index, start, end, states,
- transport_proto, verbose);
+ sf.verbose = verbose;
+ session_cli_show_session_filter (vm, &sf);
goto done;
}
session_cli_show_all_sessions (vm, verbose);
done:
- vec_free (states);
+ vec_free (sf.states);
return error;
}
-VLIB_CLI_COMMAND (vlib_cli_show_session_command) =
-{
+VLIB_CLI_COMMAND (vlib_cli_show_session_command) = {
.path = "show session",
- .short_help = "show session [verbose [n]] [listeners <proto>] "
- "[<session-id> [elog]] [thread <n> [index <n>] "
- "[proto <proto>] [state <state>] [range <min> [<max>]] "
- "[protos] [states] ",
+ .short_help =
+ "show session [protos][states][rt-backend][verbose [n]] "
+ "[events][listeners <proto>] "
+ "[<session-id>][thread <n> [[proto <p>] index <n>]][elog] "
+ "[thread <n>][proto <proto>][state <state>][range <min> [<max>]] "
+ "[lcl|rmt|ep <ip>[:<port>]][force-print]",
.function = show_session_command_fn,
};
@@ -829,29 +971,47 @@ static clib_error_t *
session_enable_disable_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
{
- u8 is_en = 2;
+ session_enable_disable_args_t args;
+ session_main_t *smm = &session_main;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "enable"))
- is_en = 1;
+ {
+ args.is_en = 1;
+ if (unformat (input, "rt-backend"))
+ if (unformat (input, "sdl"))
+ args.rt_engine_type = RT_BACKEND_ENGINE_SDL;
+ else if (unformat (input, "rule-table"))
+ args.rt_engine_type = RT_BACKEND_ENGINE_RULE_TABLE;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ else
+ args.rt_engine_type = RT_BACKEND_ENGINE_NONE;
+ }
else if (unformat (input, "disable"))
- is_en = 0;
+ {
+ args.rt_engine_type = RT_BACKEND_ENGINE_DISABLE;
+ args.is_en = 0;
+ }
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
}
- if (is_en > 1)
- return clib_error_return (0, "expected enable | disable");
+ if (smm->is_enabled && args.is_en)
+ if (args.rt_engine_type != smm->rt_engine_type)
+ return clib_error_return (
+ 0, "session is already enable. Must disable first");
- return vnet_session_enable_disable (vm, is_en);
+ return vnet_session_enable_disable (vm, &args);
}
-VLIB_CLI_COMMAND (session_enable_disable_command, static) =
-{
+VLIB_CLI_COMMAND (session_enable_disable_command, static) = {
.path = "session",
- .short_help = "session [enable|disable]",
+ .short_help =
+ "session { enable [ rt-backend sdl | rule-table ] } | { disable }",
.function = session_enable_disable_fn,
};
diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c
index 9d028dbb28c..0d580ba35c6 100644
--- a/src/vnet/session/session_lookup.c
+++ b/src/vnet/session/session_lookup.c
@@ -28,6 +28,7 @@
#include <vnet/session/session_lookup.h>
#include <vnet/session/session.h>
#include <vnet/session/application.h>
+#include <vnet/session/session_rules_table.h>
static session_lookup_main_t sl_main;
@@ -37,6 +38,8 @@ static session_lookup_main_t sl_main;
*/
static u32 *fib_index_to_table_index[2];
+static u32 *fib_index_to_lock_count[2];
+
/* 16 octets */
typedef CLIB_PACKED (struct {
union
@@ -239,7 +242,7 @@ session_table_get_for_connection (transport_connection_t * tc)
session_table_get (fib_index_to_table_index[fib_proto][tc->fib_index]);
}
-static session_table_t *
+session_table_t *
session_table_get_for_fib_index (u32 fib_proto, u32 fib_index)
{
if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
@@ -476,10 +479,10 @@ session_lookup_rules_table_session4 (session_table_t * st, u8 proto,
ip4_address_t * lcl, u16 lcl_port,
ip4_address_t * rmt, u16 rmt_port)
{
- session_rules_table_t *srt = &st->session_rules[proto];
u32 action_index, app_index;
- action_index = session_rules_table_lookup4 (srt, lcl, rmt, lcl_port,
- rmt_port);
+
+ action_index = session_rules_table_lookup4 (st->srtg_handle, proto, lcl, rmt,
+ lcl_port, rmt_port);
app_index = session_lookup_action_to_handle (action_index);
/* Nothing sophisticated for now, action index is app index */
return session_lookup_app_listen_session (app_index, FIB_PROTOCOL_IP4,
@@ -492,10 +495,10 @@ session_lookup_rules_table_session6 (session_table_t * st, u8 proto,
ip6_address_t * lcl, u16 lcl_port,
ip6_address_t * rmt, u16 rmt_port)
{
- session_rules_table_t *srt = &st->session_rules[proto];
u32 action_index, app_index;
- action_index = session_rules_table_lookup6 (srt, lcl, rmt, lcl_port,
- rmt_port);
+
+ action_index = session_rules_table_lookup6 (st->srtg_handle, proto, lcl, rmt,
+ lcl_port, rmt_port);
app_index = session_lookup_action_to_handle (action_index);
return session_lookup_app_listen_session (app_index, FIB_PROTOCOL_IP6,
proto);
@@ -515,7 +518,6 @@ u64
session_lookup_endpoint_listener (u32 table_index, session_endpoint_t * sep,
u8 use_rules)
{
- session_rules_table_t *srt;
session_table_t *st;
u32 ai;
int rv;
@@ -535,10 +537,12 @@ session_lookup_endpoint_listener (u32 table_index, session_endpoint_t * sep,
return kv4.value;
if (use_rules)
{
+ if (st->srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return SESSION_INVALID_HANDLE;
clib_memset (&lcl4, 0, sizeof (lcl4));
- srt = &st->session_rules[sep->transport_proto];
- ai = session_rules_table_lookup4 (srt, &lcl4, &sep->ip.ip4, 0,
- sep->port);
+ ai =
+ session_rules_table_lookup4 (st->srtg_handle, sep->transport_proto,
+ &lcl4, &sep->ip.ip4, 0, sep->port);
if (session_lookup_action_index_is_valid (ai))
return session_lookup_action_to_handle (ai);
}
@@ -556,10 +560,12 @@ session_lookup_endpoint_listener (u32 table_index, session_endpoint_t * sep,
if (use_rules)
{
+ if (st->srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return SESSION_INVALID_HANDLE;
clib_memset (&lcl6, 0, sizeof (lcl6));
- srt = &st->session_rules[sep->transport_proto];
- ai = session_rules_table_lookup6 (srt, &lcl6, &sep->ip.ip6, 0,
- sep->port);
+ ai =
+ session_rules_table_lookup6 (st->srtg_handle, sep->transport_proto,
+ &lcl6, &sep->ip.ip6, 0, sep->port);
if (session_lookup_action_index_is_valid (ai))
return session_lookup_action_to_handle (ai);
}
@@ -586,14 +592,13 @@ session_lookup_endpoint_listener (u32 table_index, session_endpoint_t * sep,
u64
session_lookup_local_endpoint (u32 table_index, session_endpoint_t * sep)
{
- session_rules_table_t *srt;
session_table_t *st;
u32 ai;
int rv;
st = session_table_get (table_index);
if (!st)
- return SESSION_INVALID_INDEX;
+ return SESSION_INVALID_HANDLE;
ASSERT (st->is_local);
if (sep->is_ip4)
@@ -601,15 +606,18 @@ session_lookup_local_endpoint (u32 table_index, session_endpoint_t * sep)
session_kv4_t kv4;
ip4_address_t lcl4;
- /*
- * Check if endpoint has special rules associated
- */
- clib_memset (&lcl4, 0, sizeof (lcl4));
- srt = &st->session_rules[sep->transport_proto];
- ai = session_rules_table_lookup4 (srt, &lcl4, &sep->ip.ip4, 0,
- sep->port);
- if (session_lookup_action_index_is_valid (ai))
- return session_lookup_action_to_handle (ai);
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
+ {
+ /*
+ * Check if endpoint has special rules associated
+ */
+ clib_memset (&lcl4, 0, sizeof (lcl4));
+ ai =
+ session_rules_table_lookup4 (st->srtg_handle, sep->transport_proto,
+ &lcl4, &sep->ip.ip4, 0, sep->port);
+ if (session_lookup_action_index_is_valid (ai))
+ return session_lookup_action_to_handle (ai);
+ }
/*
* Check if session endpoint is a listener
@@ -649,12 +657,15 @@ session_lookup_local_endpoint (u32 table_index, session_endpoint_t * sep)
session_kv6_t kv6;
ip6_address_t lcl6;
- clib_memset (&lcl6, 0, sizeof (lcl6));
- srt = &st->session_rules[sep->transport_proto];
- ai = session_rules_table_lookup6 (srt, &lcl6, &sep->ip.ip6, 0,
- sep->port);
- if (session_lookup_action_index_is_valid (ai))
- return session_lookup_action_to_handle (ai);
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
+ {
+ clib_memset (&lcl6, 0, sizeof (lcl6));
+ ai =
+ session_rules_table_lookup6 (st->srtg_handle, sep->transport_proto,
+ &lcl6, &sep->ip.ip6, 0, sep->port);
+ if (session_lookup_action_index_is_valid (ai))
+ return session_lookup_action_to_handle (ai);
+ }
make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
sep->transport_proto);
@@ -982,22 +993,25 @@ session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl,
if (rv == 0)
return transport_get_half_open (proto, kv4.value & 0xFFFFFFFF);
- /*
- * Check the session rules table
- */
- action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
- rmt, lcl_port, rmt_port);
- if (session_lookup_action_index_is_valid (action_index))
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
{
- if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ /*
+ * Check the session rules table
+ */
+ action_index = session_rules_table_lookup4 (st->srtg_handle, proto, lcl,
+ rmt, lcl_port, rmt_port);
+ if (session_lookup_action_index_is_valid (action_index))
{
- *result = SESSION_LOOKUP_RESULT_FILTERED;
+ if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ {
+ *result = SESSION_LOOKUP_RESULT_FILTERED;
+ return 0;
+ }
+ if ((s = session_lookup_action_to_session (action_index,
+ FIB_PROTOCOL_IP4, proto)))
+ return transport_get_listener (proto, s->connection_index);
return 0;
}
- if ((s = session_lookup_action_to_session (action_index,
- FIB_PROTOCOL_IP4, proto)))
- return transport_get_listener (proto, s->connection_index);
- return 0;
}
/*
@@ -1059,19 +1073,22 @@ session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl,
if (rv == 0)
return transport_get_half_open (proto, kv4.value & 0xFFFFFFFF);
- /*
- * Check the session rules table
- */
- action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
- rmt, lcl_port, rmt_port);
- if (session_lookup_action_index_is_valid (action_index))
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
{
- if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
- return 0;
- if ((s = session_lookup_action_to_session (action_index,
- FIB_PROTOCOL_IP4, proto)))
- return transport_get_listener (proto, s->connection_index);
- return 0;
+ /*
+ * Check the session rules table
+ */
+ action_index = session_rules_table_lookup4 (st->srtg_handle, proto, lcl,
+ rmt, lcl_port, rmt_port);
+ if (session_lookup_action_index_is_valid (action_index))
+ {
+ if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ return 0;
+ if ((s = session_lookup_action_to_session (action_index,
+ FIB_PROTOCOL_IP4, proto)))
+ return transport_get_listener (proto, s->connection_index);
+ return 0;
+ }
}
/*
@@ -1117,17 +1134,20 @@ session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
if (rv == 0)
return session_get_from_handle_safe (kv4.value);
- /*
- * Check the session rules table
- */
- action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
- rmt, lcl_port, rmt_port);
- if (session_lookup_action_index_is_valid (action_index))
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
{
- if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
- return 0;
- return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP4,
- proto);
+ /*
+ * Check the session rules table
+ */
+ action_index = session_rules_table_lookup4 (st->srtg_handle, proto, lcl,
+ rmt, lcl_port, rmt_port);
+ if (session_lookup_action_index_is_valid (action_index))
+ {
+ if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ return 0;
+ return session_lookup_action_to_session (action_index,
+ FIB_PROTOCOL_IP4, proto);
+ }
}
/*
@@ -1184,7 +1204,6 @@ session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
if (rv == 0)
{
- ASSERT ((u32) (kv6.value >> 32) == thread_index);
if (PREDICT_FALSE ((u32) (kv6.value >> 32) != thread_index))
{
*result = SESSION_LOOKUP_RESULT_WRONG_THREAD;
@@ -1200,20 +1219,23 @@ session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
if (rv == 0)
return transport_get_half_open (proto, kv6.value & 0xFFFFFFFF);
- /* Check the session rules table */
- action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
- rmt, lcl_port, rmt_port);
- if (session_lookup_action_index_is_valid (action_index))
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
{
- if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ /* Check the session rules table */
+ action_index = session_rules_table_lookup6 (st->srtg_handle, proto, lcl,
+ rmt, lcl_port, rmt_port);
+ if (session_lookup_action_index_is_valid (action_index))
{
- *result = SESSION_LOOKUP_RESULT_FILTERED;
+ if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ {
+ *result = SESSION_LOOKUP_RESULT_FILTERED;
+ return 0;
+ }
+ if ((s = session_lookup_action_to_session (action_index,
+ FIB_PROTOCOL_IP6, proto)))
+ return transport_get_listener (proto, s->connection_index);
return 0;
}
- if ((s = session_lookup_action_to_session (action_index,
- FIB_PROTOCOL_IP6, proto)))
- return transport_get_listener (proto, s->connection_index);
- return 0;
}
/* If nothing is found, check if any listener is available */
@@ -1269,17 +1291,20 @@ session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
if (rv == 0)
return transport_get_half_open (proto, kv6.value & 0xFFFFFFFF);
- /* Check the session rules table */
- action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
- rmt, lcl_port, rmt_port);
- if (session_lookup_action_index_is_valid (action_index))
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
{
- if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
- return 0;
- if ((s = session_lookup_action_to_session (action_index,
- FIB_PROTOCOL_IP6, proto)))
- return transport_get_listener (proto, s->connection_index);
- return 0;
+ /* Check the session rules table */
+ action_index = session_rules_table_lookup6 (st->srtg_handle, proto, lcl,
+ rmt, lcl_port, rmt_port);
+ if (session_lookup_action_index_is_valid (action_index))
+ {
+ if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ return 0;
+ if ((s = session_lookup_action_to_session (action_index,
+ FIB_PROTOCOL_IP6, proto)))
+ return transport_get_listener (proto, s->connection_index);
+ return 0;
+ }
}
/* If nothing is found, check if any listener is available */
@@ -1322,15 +1347,18 @@ session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
if (rv == 0)
return session_get_from_handle_safe (kv6.value);
- /* Check the session rules table */
- action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
- rmt, lcl_port, rmt_port);
- if (session_lookup_action_index_is_valid (action_index))
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
{
- if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
- return 0;
- return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP6,
- proto);
+ /* Check the session rules table */
+ action_index = session_rules_table_lookup6 (st->srtg_handle, proto, lcl,
+ rmt, lcl_port, rmt_port);
+ if (session_lookup_action_index_is_valid (action_index))
+ {
+ if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
+ return 0;
+ return session_lookup_action_to_session (action_index,
+ FIB_PROTOCOL_IP6, proto);
+ }
}
/* If nothing is found, check if any listener is available */
@@ -1356,7 +1384,6 @@ session_error_t
vnet_session_rule_add_del (session_rule_add_del_args_t *args)
{
app_namespace_t *app_ns = app_namespace_get (args->appns_index);
- session_rules_table_t *srt;
session_table_t *st;
u32 fib_index;
u8 fib_proto;
@@ -1377,8 +1404,9 @@ vnet_session_rule_add_del (session_rule_add_del_args_t *args)
fib_proto = args->table_args.rmt.fp_proto;
fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
st = session_table_get_for_fib_index (fib_proto, fib_index);
- srt = &st->session_rules[args->transport_proto];
- if ((rv = session_rules_table_add_del (srt, &args->table_args)))
+ session_rules_table_init (st, fib_proto);
+ if ((rv = session_rules_table_add_del (
+ st->srtg_handle, args->transport_proto, &args->table_args)))
return rv;
}
if (args->scope & SESSION_RULE_SCOPE_LOCAL)
@@ -1387,12 +1415,30 @@ vnet_session_rule_add_del (session_rule_add_del_args_t *args)
args->table_args.lcl.fp_proto = args->table_args.rmt.fp_proto;
args->table_args.lcl_port = 0;
st = app_namespace_get_local_table (app_ns);
- srt = &st->session_rules[args->transport_proto];
- rv = session_rules_table_add_del (srt, &args->table_args);
+ session_rules_table_init (st, args->table_args.rmt.fp_proto);
+ rv = session_rules_table_add_del (st->srtg_handle, args->transport_proto,
+ &args->table_args);
}
return rv;
}
+static void
+session_lookup_fib_table_lock (u32 fib_index, u32 protocol)
+{
+ fib_table_lock (fib_index, protocol, sl_main.fib_src);
+ vec_validate (fib_index_to_lock_count[protocol], fib_index);
+ fib_index_to_lock_count[protocol][fib_index]++;
+ ASSERT (fib_index_to_lock_count[protocol][fib_index] > 0);
+}
+
+static void
+session_lookup_fib_table_unlock (u32 fib_index, u32 protocol)
+{
+ fib_table_unlock (fib_index, protocol, sl_main.fib_src);
+ ASSERT (fib_index_to_lock_count[protocol][fib_index] > 0);
+ fib_index_to_lock_count[protocol][fib_index]--;
+}
+
/**
* Mark (global) tables as pertaining to app ns
*/
@@ -1408,7 +1454,10 @@ session_lookup_set_tables_appns (app_namespace_t * app_ns)
fib_index = app_namespace_get_fib_index (app_ns, fp);
st = session_table_get_or_alloc (fp, fib_index);
if (st)
- st->appns_index = app_namespace_index (app_ns);
+ {
+ st->appns_index = app_namespace_index (app_ns);
+ session_lookup_fib_table_lock (fib_index, fp);
+ }
}
}
@@ -1502,6 +1551,9 @@ session_rule_command_fn (vlib_main_t * vm, unformat_input_t * input,
session_cli_return_if_not_enabled ();
+ if (session_rule_table_is_enabled () == 0)
+ return clib_error_return (0, "session rule table engine is not enabled");
+
clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -1623,11 +1675,12 @@ session_lookup_dump_rules_table (u32 fib_index, u8 fib_proto,
u8 transport_proto)
{
vlib_main_t *vm = vlib_get_main ();
- session_rules_table_t *srt;
session_table_t *st;
st = session_table_get_for_fib_index (fib_index, fib_proto);
- srt = &st->session_rules[transport_proto];
- session_rules_table_cli_dump (vm, srt, fib_proto);
+ if (st == 0)
+ return;
+ session_rules_table_cli_dump (vm, st->srtg_handle, transport_proto,
+ fib_proto);
}
void
@@ -1635,11 +1688,12 @@ session_lookup_dump_local_rules_table (u32 table_index, u8 fib_proto,
u8 transport_proto)
{
vlib_main_t *vm = vlib_get_main ();
- session_rules_table_t *srt;
session_table_t *st;
st = session_table_get (table_index);
- srt = &st->session_rules[transport_proto];
- session_rules_table_cli_dump (vm, srt, fib_proto);
+ if (st == 0)
+ return;
+ session_rules_table_cli_dump (vm, st->srtg_handle, transport_proto,
+ fib_proto);
}
static clib_error_t *
@@ -1651,7 +1705,6 @@ show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
ip46_address_t lcl_ip, rmt_ip;
u8 is_ip4 = 1, show_one = 0;
app_namespace_t *app_ns;
- session_rules_table_t *srt;
session_table_t *st;
u8 *ns_id = 0, fib_proto;
@@ -1666,9 +1719,9 @@ show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
else if (unformat (input, "appns %_%v%_", &ns_id))
;
else if (unformat (input, "scope global"))
- scope = 1;
+ scope = SESSION_RULE_SCOPE_GLOBAL;
else if (unformat (input, "scope local"))
- scope = 2;
+ scope = SESSION_RULE_SCOPE_LOCAL;
else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
&lcl_ip.ip4, &lcl_plen, &lcl_port,
unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
@@ -1686,14 +1739,17 @@ show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
show_one = 1;
}
else
- return clib_error_return (0, "unknown input `%U'",
- format_unformat_error, input);
+ {
+ vec_free (ns_id);
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
}
if (transport_proto == ~0)
{
vlib_cli_output (vm, "transport proto must be set");
- return 0;
+ goto done;
}
if (ns_id)
@@ -1702,7 +1758,7 @@ show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
if (!app_ns)
{
vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
- return 0;
+ goto done;
}
}
else
@@ -1710,7 +1766,7 @@ show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
app_ns = app_namespace_get_default ();
}
- if (scope == 1 || scope == 0)
+ if (scope == SESSION_RULE_SCOPE_GLOBAL || scope == 0)
{
fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
fib_index = is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
@@ -1721,20 +1777,51 @@ show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
st = app_namespace_get_local_table (app_ns);
}
+ if (session_rule_table_is_enabled () == 0)
+ {
+ vlib_cli_output (vm, "session rule table engine is not enabled");
+ goto done;
+ }
+
if (show_one)
{
- srt = &st->session_rules[transport_proto];
- session_rules_table_show_rule (vm, srt, &lcl_ip, lcl_port, &rmt_ip,
- rmt_port, is_ip4);
- return 0;
+ if (st)
+ session_rules_table_show_rule (vm, st->srtg_handle, transport_proto,
+ &lcl_ip, lcl_port, &rmt_ip, rmt_port,
+ is_ip4);
+ goto done;
}
vlib_cli_output (vm, "%U rules table", format_transport_proto,
transport_proto);
- srt = &st->session_rules[transport_proto];
- session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4);
- session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP6);
+ if (scope == SESSION_RULE_SCOPE_LOCAL)
+ {
+ if (st)
+ {
+ session_rules_table_cli_dump (vm, st->srtg_handle, transport_proto,
+ FIB_PROTOCOL_IP4);
+ session_rules_table_cli_dump (vm, st->srtg_handle, transport_proto,
+ FIB_PROTOCOL_IP6);
+ }
+ }
+ else
+ {
+ /*
+ * 2 separate session tables for global entries, 1 for ip4 and 1 for ip6
+ */
+ st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4,
+ app_ns->ip4_fib_index);
+ if (st)
+ session_rules_table_cli_dump (vm, st->srtg_handle, transport_proto,
+ FIB_PROTOCOL_IP4);
+ st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6,
+ app_ns->ip6_fib_index);
+ if (st)
+ session_rules_table_cli_dump (vm, st->srtg_handle, transport_proto,
+ FIB_PROTOCOL_IP6);
+ }
+done:
vec_free (ns_id);
return 0;
}
@@ -1833,6 +1920,14 @@ session_lookup_init (void)
clib_spinlock_init (&slm->st_alloc_lock);
+ /* We are not contributing any route to the fib. But we allocate a fib source
+ * so that when we lock the fib table, we can view that we have a lock on the
+ * particular fib table in case we wonder why the fib table is not free after
+ * "ip table del"
+ */
+ slm->fib_src = fib_source_allocate (
+ "session lookup", FIB_SOURCE_PRIORITY_LOW, FIB_SOURCE_BH_SIMPLE);
+
/*
* Allocate default table and map it to fib_index 0
*/
@@ -1848,6 +1943,26 @@ session_lookup_init (void)
session_table_init (st, FIB_PROTOCOL_IP6);
}
+void
+session_lookup_table_cleanup (u32 fib_proto, u32 fib_index)
+{
+ session_table_t *st;
+ u32 table_index;
+
+ session_lookup_fib_table_unlock (fib_index, fib_proto);
+ if (fib_index_to_lock_count[fib_proto][fib_index] == 0)
+ {
+ table_index = session_lookup_get_index_for_fib (fib_proto, fib_index);
+ st = session_table_get (table_index);
+ if (st)
+ {
+ session_table_free (st, fib_proto);
+ if (vec_len (fib_index_to_table_index[fib_proto]) > fib_index)
+ fib_index_to_table_index[fib_proto][fib_index] = ~0;
+ }
+ }
+}
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vnet/session/session_lookup.h b/src/vnet/session/session_lookup.h
index f9ffc15165a..9f56af20a87 100644
--- a/src/vnet/session/session_lookup.h
+++ b/src/vnet/session/session_lookup.h
@@ -19,6 +19,8 @@
#include <vnet/session/session_table.h>
#include <vnet/session/session_types.h>
#include <vnet/session/application_namespace.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_source.h>
#define HALF_OPEN_LOOKUP_INVALID_VALUE ((u64)~0)
@@ -32,6 +34,7 @@ typedef enum session_lookup_result_
typedef struct session_lookup_main_
{
clib_spinlock_t st_alloc_lock;
+ fib_source_t fib_src;
} session_lookup_main_t;
session_t *session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl,
@@ -114,6 +117,17 @@ typedef enum _session_rule_scope
SESSION_RULE_SCOPE_LOCAL = 2,
} session_rule_scope_e;
+typedef struct _session_rules_table_add_del_args
+{
+ fib_prefix_t lcl;
+ fib_prefix_t rmt;
+ u16 lcl_port;
+ u16 rmt_port;
+ u32 action_index;
+ u8 *tag;
+ u8 is_add;
+} session_rule_table_add_del_args_t;
+
typedef struct _session_rule_add_del_args
{
/**
@@ -139,6 +153,8 @@ session_error_t vnet_session_rule_add_del (session_rule_add_del_args_t *args);
void session_lookup_set_tables_appns (app_namespace_t * app_ns);
void session_lookup_init (void);
+session_table_t *session_table_get_for_fib_index (u32 fib_proto,
+ u32 fib_index);
#endif /* SRC_VNET_SESSION_SESSION_LOOKUP_H_ */
diff --git a/src/vnet/session/session_node.c b/src/vnet/session/session_node.c
index 0ec158fb429..a6804f7882e 100644
--- a/src/vnet/session/session_node.c
+++ b/src/vnet/session/session_node.c
@@ -2163,6 +2163,8 @@ session_queue_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
session_queue_run_on_main (vm);
break;
case SESSION_Q_PROCESS_STOP:
+ /* Free event_data, the node will be restarted if needed */
+ vec_free (event_data);
vlib_node_set_state (vm, session_queue_process_node.index,
VLIB_NODE_STATE_DISABLED);
timeout = 100000.0;
diff --git a/src/vnet/session/session_rules_table.c b/src/vnet/session/session_rules_table.c
index 70a702cf55c..acb575b0f7c 100644
--- a/src/vnet/session/session_rules_table.c
+++ b/src/vnet/session/session_rules_table.c
@@ -17,8 +17,23 @@
#include <vnet/session/mma_template.c>
#include <vnet/session/mma_40.h>
#include <vnet/session/mma_template.c>
-#include <vnet/session/session_rules_table.h>
#include <vnet/session/transport.h>
+#include <vnet/session/session.h>
+#include <vnet/session/session_table.h>
+#include <vnet/session/session_rules_table.h>
+#include <vnet/session/session_sdl.h>
+
+VLIB_REGISTER_LOG_CLASS (session_rt_log, static) = { .class_name = "session",
+ .subclass_name = "rt" };
+
+#define log_debug(fmt, ...) \
+ vlib_log_debug (session_rt_log.class, "%s: " fmt, __func__, __VA_ARGS__)
+#define log_warn(fmt, ...) \
+ vlib_log_warn (session_rt_log.class, fmt, __VA_ARGS__)
+#define log_err(fmt, ...) vlib_log_err (session_rt_log.class, fmt, __VA_ARGS__)
+
+static session_rules_table_group_t *srt_instances;
+const session_rt_engine_vft_t *session_rt_engine_vft;
u32
session_rule_tag_key_index (u32 ri, u8 is_ip4)
@@ -75,6 +90,7 @@ session_rules_table_del_tag (session_rules_table_t * srt, u8 * tag, u8 is_ip4)
ASSERT (rt);
hash_unset_mem (srt->rules_by_tag, tag);
hash_unset (srt->tags_by_rules, rti_key);
+ vec_free (rt->tag);
pool_put (srt->rule_tags, rt);
}
@@ -118,13 +134,25 @@ fib_pref_normalize (fib_prefix_t * pref)
}
u8 *
+format_session_rule_tag (u8 *s, va_list *args)
+{
+ static u8 *null_tag = 0;
+ u8 *tag = va_arg (*args, u8 *);
+
+ if (!null_tag)
+ null_tag = format (0, "none");
+ s = format (s, "%v", (tag != 0) ? tag : null_tag);
+ return s;
+}
+
+u8 *
format_session_rule4 (u8 * s, va_list * args)
{
session_rules_table_t *srt = va_arg (*args, session_rules_table_t *);
mma_rule_16_t *sr = va_arg (*args, mma_rule_16_t *);
session_mask_or_match_4_t *mask, *match;
mma_rules_table_16_t *srt4;
- u8 *tag = 0, *null_tag = format (0, "none");
+ u8 *tag = 0;
u32 ri;
int i;
@@ -134,20 +162,18 @@ format_session_rule4 (u8 * s, va_list * args)
match = (session_mask_or_match_4_t *) & sr->match;
mask = (session_mask_or_match_4_t *) & sr->mask;
- s = format (s, "[%d] rule: %U/%d %d %U/%d %d action: %d tag: %v", ri,
+ s = format (s, "[%d] rule: %U/%d %d %U/%d %d action: %d tag: %U", ri,
format_ip4_address, &match->lcl_ip,
- ip4_mask_to_preflen (&mask->lcl_ip),
- clib_net_to_host_u16 (match->lcl_port), format_ip4_address,
- &match->rmt_ip, ip4_mask_to_preflen (&mask->rmt_ip),
- clib_net_to_host_u16 (match->rmt_port), sr->action_index,
- tag ? tag : null_tag);
+ ip4_mask_to_preflen (&mask->lcl_ip), match->lcl_port,
+ format_ip4_address, &match->rmt_ip,
+ ip4_mask_to_preflen (&mask->rmt_ip), match->rmt_port,
+ sr->action_index, format_session_rule_tag, tag);
if (vec_len (sr->next_indices))
{
s = format (s, "\n children: ");
for (i = 0; i < vec_len (sr->next_indices); i++)
s = format (s, "%d ", sr->next_indices[i]);
}
- vec_free (null_tag);
return s;
}
@@ -158,7 +184,7 @@ format_session_rule6 (u8 * s, va_list * args)
mma_rule_40_t *sr = va_arg (*args, mma_rule_40_t *);
session_mask_or_match_6_t *mask, *match;
mma_rules_table_40_t *srt6;
- u8 *tag = 0, *null_tag = format (0, "none");
+ u8 *tag = 0;
u32 ri;
int i;
@@ -168,20 +194,18 @@ format_session_rule6 (u8 * s, va_list * args)
match = (session_mask_or_match_6_t *) & sr->match;
mask = (session_mask_or_match_6_t *) & sr->mask;
- s = format (s, "[%d] rule: %U/%d %d %U/%d %d action: %d tag: %v", ri,
+ s = format (s, "[%d] rule: %U/%d %d %U/%d %d action: %d tag: %U", ri,
format_ip6_address, &match->lcl_ip,
- ip6_mask_to_preflen (&mask->lcl_ip),
- clib_net_to_host_u16 (match->lcl_port), format_ip6_address,
- &match->rmt_ip, ip6_mask_to_preflen (&mask->rmt_ip),
- clib_net_to_host_u16 (match->rmt_port), sr->action_index,
- tag ? tag : null_tag);
+ ip6_mask_to_preflen (&mask->lcl_ip), match->lcl_port,
+ format_ip6_address, &match->rmt_ip,
+ ip6_mask_to_preflen (&mask->rmt_ip), match->rmt_port,
+ sr->action_index, format_session_rule_tag, tag);
if (vec_len (sr->next_indices))
{
s = format (s, "\n children: ");
for (i = 0; i < vec_len (sr->next_indices); i++)
s = format (s, "%d ", sr->next_indices[i]);
}
- vec_free (null_tag);
return s;
}
@@ -312,11 +336,10 @@ session_rules_table_alloc_rule_40 (mma_rules_table_40_t * srt,
return rule;
}
-u32
-session_rules_table_lookup_rule4 (session_rules_table_t * srt,
- ip4_address_t * lcl_ip,
- ip4_address_t * rmt_ip, u16 lcl_port,
- u16 rmt_port)
+static u32
+session_rules_table_lookup_rule4 (session_rules_table_t *srt,
+ ip4_address_t *lcl_ip, ip4_address_t *rmt_ip,
+ u16 lcl_port, u16 rmt_port)
{
mma_rules_table_16_t *srt4 = &srt->session_rules_tables_16;
session_mask_or_match_4_t key = {
@@ -325,16 +348,20 @@ session_rules_table_lookup_rule4 (session_rules_table_t * srt,
.lcl_port = lcl_port,
.rmt_port = rmt_port,
};
+
+ if (srt4->rules == 0)
+ return SESSION_TABLE_INVALID_INDEX;
return mma_rules_table_lookup_rule_16 (srt4,
(mma_mask_or_match_16_t *) & key,
srt4->root_index);
}
u32
-session_rules_table_lookup4 (session_rules_table_t * srt,
- ip4_address_t * lcl_ip, ip4_address_t * rmt_ip,
- u16 lcl_port, u16 rmt_port)
+session_rules_table_lookup4_ (u32 srtg_handle, u32 proto,
+ ip4_address_t *lcl_ip, ip4_address_t *rmt_ip,
+ u16 lcl_port, u16 rmt_port)
{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, proto);
mma_rules_table_16_t *srt4 = &srt->session_rules_tables_16;
session_mask_or_match_4_t key = {
.lcl_ip.as_u32 = lcl_ip->as_u32,
@@ -342,21 +369,36 @@ session_rules_table_lookup4 (session_rules_table_t * srt,
.lcl_port = lcl_port,
.rmt_port = rmt_port,
};
+
+ if (srt4->rules == 0)
+ return SESSION_TABLE_INVALID_INDEX;
return mma_rules_table_lookup_16 (srt4, (mma_mask_or_match_16_t *) & key,
srt4->root_index);
}
-u32
-session_rules_table_lookup_rule6 (session_rules_table_t * srt,
- ip6_address_t * lcl_ip,
- ip6_address_t * rmt_ip, u16 lcl_port,
- u16 rmt_port)
+session_rules_table_t *
+srtg_handle_to_srt (u32 srtg_handle, u32 proto)
+{
+ session_rules_table_group_t *srtg =
+ pool_elt_at_index (srt_instances, srtg_handle);
+ session_rules_table_t *srt = &srtg->session_rules[proto];
+
+ return srt;
+}
+
+static u32
+session_rules_table_lookup_rule6 (session_rules_table_t *srt,
+ ip6_address_t *lcl_ip, ip6_address_t *rmt_ip,
+ u16 lcl_port, u16 rmt_port)
{
mma_rules_table_40_t *srt6 = &srt->session_rules_tables_40;
session_mask_or_match_6_t key = {
.lcl_port = lcl_port,
.rmt_port = rmt_port,
};
+
+ if (srt6->rules == 0)
+ return SESSION_TABLE_INVALID_INDEX;
clib_memcpy_fast (&key.lcl_ip, lcl_ip, sizeof (*lcl_ip));
clib_memcpy_fast (&key.rmt_ip, rmt_ip, sizeof (*rmt_ip));
return mma_rules_table_lookup_rule_40 (srt6,
@@ -365,15 +407,19 @@ session_rules_table_lookup_rule6 (session_rules_table_t * srt,
}
u32
-session_rules_table_lookup6 (session_rules_table_t * srt,
- ip6_address_t * lcl_ip, ip6_address_t * rmt_ip,
- u16 lcl_port, u16 rmt_port)
+session_rules_table_lookup6_ (u32 srtg_handle, u32 proto,
+ ip6_address_t *lcl_ip, ip6_address_t *rmt_ip,
+ u16 lcl_port, u16 rmt_port)
{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, proto);
mma_rules_table_40_t *srt6 = &srt->session_rules_tables_40;
session_mask_or_match_6_t key = {
.lcl_port = lcl_port,
.rmt_port = rmt_port,
};
+
+ if (srt6->rules == 0)
+ return SESSION_TABLE_INVALID_INDEX;
clib_memcpy_fast (&key.lcl_ip, lcl_ip, sizeof (*lcl_ip));
clib_memcpy_fast (&key.rmt_ip, rmt_ip, sizeof (*rmt_ip));
return mma_rules_table_lookup_40 (srt6, (mma_mask_or_match_40_t *) & key,
@@ -389,9 +435,10 @@ session_rules_table_lookup6 (session_rules_table_t * srt,
* @return 0 if success, session_error_t error otherwise
*/
session_error_t
-session_rules_table_add_del (session_rules_table_t *srt,
- session_rule_table_add_del_args_t *args)
+session_rules_table_add_del_ (u32 srtg_handle, u32 proto,
+ session_rule_table_add_del_args_t *args)
{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, proto);
u8 fib_proto = args->rmt.fp_proto, *rt;
u32 ri_from_tag, ri;
int rv;
@@ -514,47 +561,90 @@ session_rules_table_add_del (session_rules_table_t *srt,
}
void
-session_rules_table_free (session_rules_table_t *srt)
+session_rules_table_free_ (session_table_t *st, u8 fib_proto)
+{
+ session_rules_table_group_t *srtg =
+ pool_elt_at_index (srt_instances, st->srtg_handle);
+ session_rules_table_t *srt;
+
+ vec_foreach (srt, srtg->session_rules)
+ {
+ mma_rules_table_free_16 (&srt->session_rules_tables_16);
+ mma_rules_table_free_40 (&srt->session_rules_tables_40);
+
+ hash_free (srt->tags_by_rules);
+ hash_free (srt->rules_by_tag);
+ }
+ srtg_instance_free (st);
+}
+
+void
+srtg_instance_free (session_table_t *st)
{
- mma_rules_table_free_16 (&srt->session_rules_tables_16);
- mma_rules_table_free_40 (&srt->session_rules_tables_40);
+ session_rules_table_group_t *srtg =
+ pool_elt_at_index (srt_instances, st->srtg_handle);
+
+ vec_free (srtg->session_rules);
+ pool_put (srt_instances, srtg);
+ st->srtg_handle = SESSION_SRTG_HANDLE_INVALID;
+}
+
+session_rules_table_group_t *
+srtg_instance_alloc (session_table_t *st, u32 n_proto)
+{
+ session_rules_table_group_t *srtg;
+
+ pool_get (srt_instances, srtg);
+ vec_validate (srtg->session_rules, n_proto);
+ st->srtg_handle = srtg - srt_instances;
+ return (srtg);
}
void
-session_rules_table_init (session_rules_table_t * srt)
+session_rules_table_init_ (session_table_t *st, u8 fib_proto)
{
mma_rules_table_16_t *srt4;
mma_rules_table_40_t *srt6;
mma_rule_16_t *rule4;
mma_rule_40_t *rule6;
fib_prefix_t null_prefix;
+ session_rules_table_t *srt;
+ session_rules_table_group_t *srtg;
- clib_memset (&null_prefix, 0, sizeof (null_prefix));
+ srtg = srtg_instance_alloc (st, TRANSPORT_N_PROTOS - 1);
- srt4 = &srt->session_rules_tables_16;
- rule4 = session_rules_table_alloc_rule_16 (srt4, &null_prefix, 0,
- &null_prefix, 0);
- rule4->action_index = SESSION_RULES_TABLE_INVALID_INDEX;
- srt4->root_index = mma_rules_table_rule_index_16 (srt4, rule4);
- srt4->rule_cmp_fn = rule_cmp_16;
+ clib_memset (&null_prefix, 0, sizeof (null_prefix));
+ vec_foreach (srt, srtg->session_rules)
+ {
+ srt4 = &srt->session_rules_tables_16;
- srt6 = &srt->session_rules_tables_40;
- rule6 = session_rules_table_alloc_rule_40 (srt6, &null_prefix, 0,
- &null_prefix, 0);
- rule6->action_index = SESSION_RULES_TABLE_INVALID_INDEX;
- srt6->root_index = mma_rules_table_rule_index_40 (srt6, rule6);
- srt6->rule_cmp_fn = rule_cmp_40;
+ ASSERT (srt4->rules == 0);
+ rule4 = session_rules_table_alloc_rule_16 (srt4, &null_prefix, 0,
+ &null_prefix, 0);
+ rule4->action_index = SESSION_RULES_TABLE_INVALID_INDEX;
+ srt4->root_index = mma_rules_table_rule_index_16 (srt4, rule4);
+ srt4->rule_cmp_fn = rule_cmp_16;
- srt->rules_by_tag = hash_create_vec (0, sizeof (u8), sizeof (uword));
- srt->tags_by_rules = hash_create (0, sizeof (uword));
+ srt6 = &srt->session_rules_tables_40;
+ ASSERT (srt6->rules == 0);
+ rule6 = session_rules_table_alloc_rule_40 (srt6, &null_prefix, 0,
+ &null_prefix, 0);
+ rule6->action_index = SESSION_RULES_TABLE_INVALID_INDEX;
+ srt6->root_index = mma_rules_table_rule_index_40 (srt6, rule6);
+ srt6->rule_cmp_fn = rule_cmp_40;
+
+ srt->rules_by_tag = hash_create_vec (0, sizeof (u8), sizeof (uword));
+ srt->tags_by_rules = hash_create (0, sizeof (uword));
+ }
}
void
-session_rules_table_show_rule (vlib_main_t * vm, session_rules_table_t * srt,
- ip46_address_t * lcl_ip, u16 lcl_port,
- ip46_address_t * rmt_ip, u16 rmt_port,
- u8 is_ip4)
+session_rules_table_show_rule_ (vlib_main_t *vm, u32 srtg_handle, u32 proto,
+ ip46_address_t *lcl_ip, u16 lcl_port,
+ ip46_address_t *rmt_ip, u16 rmt_port,
+ u8 is_ip4)
{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, proto);
mma_rules_table_16_t *srt4;
mma_rules_table_40_t *srt6;
mma_rule_16_t *sr4;
@@ -595,9 +685,10 @@ session_rules_table_show_rule (vlib_main_t * vm, session_rules_table_t * srt,
}
void
-session_rules_table_cli_dump (vlib_main_t * vm, session_rules_table_t * srt,
- u8 fib_proto)
+session_rules_table_cli_dump_ (vlib_main_t *vm, u32 srtg_handle, u32 proto,
+ u8 fib_proto)
{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, proto);
if (fib_proto == FIB_PROTOCOL_IP4)
{
mma_rules_table_16_t *srt4;
@@ -624,6 +715,108 @@ session_rules_table_cli_dump (vlib_main_t * vm, session_rules_table_t * srt,
}
}
+static const session_rt_engine_vft_t session_rules_table_vft = {
+ .backend_engine = RT_BACKEND_ENGINE_RULE_TABLE,
+ .table_lookup4 = session_rules_table_lookup4_,
+ .table_lookup6 = session_rules_table_lookup6_,
+ .table_cli_dump = session_rules_table_cli_dump_,
+ .table_show_rule = session_rules_table_show_rule_,
+ .table_add_del = session_rules_table_add_del_,
+ .table_init = session_rules_table_init_,
+ .table_free = session_rules_table_free_,
+};
+
+static void
+session_rules_table_app_namespace_walk_cb (app_namespace_t *app_ns, void *ctx)
+{
+ u32 fib_index, table_index;
+ session_table_t *st;
+
+ log_debug ("disable app_ns %s", app_ns->ns_id);
+ st = session_table_get (app_ns->local_table_index);
+ if (st)
+ session_rules_table_free (st, FIB_PROTOCOL_MAX);
+
+ fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP4);
+ table_index = session_lookup_get_index_for_fib (FIB_PROTOCOL_IP4, fib_index);
+ st = session_table_get (table_index);
+ if (st)
+ session_rules_table_free (st, FIB_PROTOCOL_IP4);
+
+ fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP6);
+ table_index = session_lookup_get_index_for_fib (FIB_PROTOCOL_IP6, fib_index);
+ st = session_table_get (table_index);
+ if (st)
+ session_rules_table_free (st, FIB_PROTOCOL_IP6);
+}
+
+clib_error_t *
+session_rules_table_enable_disable (int enable)
+{
+ clib_error_t *error;
+
+ if (enable)
+ error = session_rule_table_register_engine (&session_rules_table_vft);
+ else
+ {
+ app_namespace_walk (session_rules_table_app_namespace_walk_cb, 0);
+ error = session_rule_table_deregister_engine (&session_rules_table_vft);
+ }
+
+ return error;
+}
+
+clib_error_t *
+session_rt_backend_enable_disable (session_rt_engine_type_t rt_engine_type)
+{
+ session_main_t *smm = &session_main;
+ clib_error_t *error = 0;
+
+ if (rt_engine_type < RT_BACKEND_ENGINE_DISABLE ||
+ rt_engine_type > RT_BACKEND_ENGINE_SDL)
+ return clib_error_return (0, "invalid rt-backend %d", rt_engine_type);
+
+ if (rt_engine_type == RT_BACKEND_ENGINE_SDL)
+ error = session_sdl_enable_disable (1);
+ else if (rt_engine_type == RT_BACKEND_ENGINE_RULE_TABLE)
+ error = session_rules_table_enable_disable (1);
+ else if (rt_engine_type == RT_BACKEND_ENGINE_DISABLE)
+ {
+ if (session_sdl_is_enabled ())
+ error = session_sdl_enable_disable (0);
+ else if (session_rule_table_is_enabled ())
+ error = session_rules_table_enable_disable (0);
+ }
+
+ if (!error)
+ smm->rt_engine_type = rt_engine_type;
+ return error;
+}
+
+clib_error_t *
+session_rule_table_register_engine (const session_rt_engine_vft_t *vft)
+{
+ if (session_rt_engine_vft == vft)
+ return 0;
+ if (session_rt_engine_vft)
+ return clib_error_return (0, "session rule engine is already registered");
+
+ session_rt_engine_vft = vft;
+ return 0;
+}
+
+clib_error_t *
+session_rule_table_deregister_engine (const session_rt_engine_vft_t *vft)
+{
+ if (session_rt_engine_vft == vft)
+ session_rt_engine_vft = 0;
+ else
+ return clib_error_return (
+ 0, "session rule engine is not registered to this engine");
+
+ return 0;
+}
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vnet/session/session_rules_table.h b/src/vnet/session/session_rules_table.h
index 010d50a6398..a61438bb9fd 100644
--- a/src/vnet/session/session_rules_table.h
+++ b/src/vnet/session/session_rules_table.h
@@ -22,6 +22,10 @@
#include <vnet/session/transport.h>
#include <vnet/session/mma_16.h>
#include <vnet/session/mma_40.h>
+#include <vnet/session/session_lookup.h>
+#include <vnet/session/session_table.h>
+
+#define SESSION_SRTG_HANDLE_INVALID ~0
typedef CLIB_PACKED (struct
{
@@ -58,29 +62,36 @@ typedef CLIB_PACKED (struct
#define SESSION_RULES_TABLE_ACTION_DROP (MMA_TABLE_INVALID_INDEX - 1)
#define SESSION_RULES_TABLE_ACTION_ALLOW (MMA_TABLE_INVALID_INDEX - 2)
-typedef struct _session_rules_table_add_del_args
-{
- fib_prefix_t lcl;
- fib_prefix_t rmt;
- u16 lcl_port;
- u16 rmt_port;
- u32 action_index;
- u8 *tag;
- u8 is_add;
-} session_rule_table_add_del_args_t;
-
typedef struct _rule_tag
{
u8 *tag;
} session_rule_tag_t;
+typedef struct session_sdl_block
+{
+ u32 ip_table_id;
+ u32 ip6_table_id;
+ u32 ip_fib_index;
+ u32 ip6_fib_index;
+} session_sdl_block_t;
+
typedef struct _session_rules_table_t
{
- /**
- * Per fib proto session rules tables
- */
- mma_rules_table_16_t session_rules_tables_16;
- mma_rules_table_40_t session_rules_tables_40;
+ union
+ {
+ /**
+ * Per fib proto session rules tables
+ */
+ struct
+ {
+ mma_rules_table_16_t session_rules_tables_16;
+ mma_rules_table_40_t session_rules_tables_40;
+ };
+ /**
+ * sdl table
+ */
+ struct session_sdl_block sdl_block;
+ };
/**
* Hash table that maps tags to rules
*/
@@ -95,28 +106,163 @@ typedef struct _session_rules_table_t
uword *tags_by_rules;
} session_rules_table_t;
-u32 session_rules_table_lookup4 (session_rules_table_t * srt,
- ip4_address_t * lcl_ip,
- ip4_address_t * rmt_ip, u16 lcl_port,
- u16 rmt_port);
-u32 session_rules_table_lookup6 (session_rules_table_t * srt,
- ip6_address_t * lcl_ip,
- ip6_address_t * rmt_ip, u16 lcl_port,
- u16 rmt_port);
-void session_rules_table_cli_dump (vlib_main_t * vm,
- session_rules_table_t * srt, u8 fib_proto);
-void session_rules_table_show_rule (vlib_main_t * vm,
- session_rules_table_t * srt,
- ip46_address_t * lcl_ip, u16 lcl_port,
- ip46_address_t * rmt_ip, u16 rmt_port,
- u8 is_ip4);
+typedef struct _session_rules_table_group_t
+{
+ session_rules_table_t *session_rules;
+} session_rules_table_group_t;
+
session_error_t
-session_rules_table_add_del (session_rules_table_t *srt,
- session_rule_table_add_del_args_t *args);
+session_rules_table_add_del_ (u32 srtg_handle, u32 proto,
+ session_rule_table_add_del_args_t *args);
u8 *session_rules_table_rule_tag (session_rules_table_t * srt, u32 ri,
u8 is_ip4);
-void session_rules_table_init (session_rules_table_t * srt);
-void session_rules_table_free (session_rules_table_t *srt);
+void session_rules_table_init_ (struct _session_lookup_table *st,
+ u8 fib_proto);
+void session_rules_table_free_ (struct _session_lookup_table *st,
+ u8 fib_proto);
+
+typedef u32 (*rules_table_lookup4) (u32 srtg_handle, u32 proto,
+ ip4_address_t *lcl_ip,
+ ip4_address_t *rmt_ip, u16 lcl_port,
+ u16 rmt_port);
+typedef u32 (*rules_table_lookup6) (u32 srtg_handle, u32 proto,
+ ip6_address_t *lcl_ip,
+ ip6_address_t *rmt_ip, u16 lcl_port,
+ u16 rmt_port);
+typedef void (*rules_table_cli_dump) (vlib_main_t *vm, u32 srtg_handle,
+ u32 proto, u8 fib_proto);
+typedef void (*rules_table_show_rule) (vlib_main_t *vm, u32 srtg_handle,
+ u32 proto, ip46_address_t *lcl_ip,
+ u16 lcl_port, ip46_address_t *rmt_ip,
+ u16 rmt_port, u8 is_ip4);
+typedef session_error_t (*rules_table_add_del) (
+ u32 srtg_handle, u32 proto, session_rule_table_add_del_args_t *args);
+typedef void (*rules_table_init) (struct _session_lookup_table *st,
+ u8 fib_proto);
+typedef void (*rules_table_free) (struct _session_lookup_table *st,
+ u8 fib_proto);
+
+#define foreach_session_rt_engine_vft_method_name \
+ _ (lookup4) \
+ _ (lookup6) \
+ _ (cli_dump) \
+ _ (show_rule) \
+ _ (add_del) \
+ _ (init) \
+ _ (free)
+
+#define _(name) rules_table_##name table_##name;
+typedef struct session_rt_engine_vft
+{
+ u32 backend_engine;
+ foreach_session_rt_engine_vft_method_name
+} session_rt_engine_vft_t;
+#undef _
+
+extern u8 *format_session_rule_tag (u8 *s, va_list *args);
+extern u8 *session_rules_table_rule_tag (session_rules_table_t *srt, u32 ri,
+ u8 is_ip4);
+extern u32 session_rules_table_rule_for_tag (session_rules_table_t *srt,
+ u8 *tag);
+extern void session_rules_table_add_tag (session_rules_table_t *srt, u8 *tag,
+ u32 rule_index, u8 is_ip4);
+extern void session_rules_table_del_tag (session_rules_table_t *srt, u8 *tag,
+ u8 is_ip4);
+
+extern const session_rt_engine_vft_t *session_rt_engine_vft;
+extern clib_error_t *session_rules_table_enable_disable (int enable);
+extern clib_error_t *
+session_rt_backend_enable_disable (session_rt_engine_type_t rt_engine_type);
+
+static_always_inline void
+session_rules_table_init (struct _session_lookup_table *st, u8 fib_proto)
+{
+ if (!session_rt_engine_vft)
+ return;
+ if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
+ return;
+ session_rt_engine_vft->table_init (st, fib_proto);
+}
+
+static_always_inline void
+session_rules_table_free (struct _session_lookup_table *st, u8 fib_proto)
+{
+ if (!session_rt_engine_vft)
+ return;
+ if (st->srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return;
+ session_rt_engine_vft->table_free (st, fib_proto);
+}
+
+static_always_inline void
+session_rules_table_show_rule (vlib_main_t *vm, u32 srtg_handle, u32 proto,
+ ip46_address_t *lcl_ip, u16 lcl_port,
+ ip46_address_t *rmt_ip, u16 rmt_port, u8 is_ip4)
+{
+ if (!session_rt_engine_vft)
+ return;
+ if (srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return;
+ session_rt_engine_vft->table_show_rule (vm, srtg_handle, proto, lcl_ip,
+ lcl_port, rmt_ip, rmt_port, is_ip4);
+}
+
+static_always_inline u32
+session_rules_table_lookup6 (u32 srtg_handle, u32 proto, ip6_address_t *lcl_ip,
+ ip6_address_t *rmt_ip, u16 lcl_port, u16 rmt_port)
+{
+ if (!session_rt_engine_vft)
+ return SESSION_RULES_TABLE_ACTION_ALLOW;
+ if (srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return SESSION_RULES_TABLE_ACTION_ALLOW;
+ return session_rt_engine_vft->table_lookup6 (srtg_handle, proto, lcl_ip,
+ rmt_ip, lcl_port, rmt_port);
+}
+
+static_always_inline void
+session_rules_table_cli_dump (vlib_main_t *vm, u32 srtg_handle, u32 proto,
+ u8 fib_proto)
+{
+ if (!session_rt_engine_vft)
+ return;
+ if (srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return;
+ session_rt_engine_vft->table_cli_dump (vm, srtg_handle, proto, fib_proto);
+}
+
+static_always_inline u32
+session_rules_table_lookup4 (u32 srtg_handle, u32 proto, ip4_address_t *lcl_ip,
+ ip4_address_t *rmt_ip, u16 lcl_port, u16 rmt_port)
+{
+ if (!session_rt_engine_vft)
+ return SESSION_RULES_TABLE_ACTION_ALLOW;
+ if (srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return SESSION_RULES_TABLE_ACTION_ALLOW;
+ return session_rt_engine_vft->table_lookup4 (srtg_handle, proto, lcl_ip,
+ rmt_ip, lcl_port, rmt_port);
+}
+
+static_always_inline session_error_t
+session_rules_table_add_del (u32 srtg_handle, u32 proto,
+ session_rule_table_add_del_args_t *args)
+{
+ if (!session_rt_engine_vft)
+ return SESSION_E_NOSUPPORT;
+ if (srtg_handle == SESSION_SRTG_HANDLE_INVALID)
+ return SESSION_E_NOSUPPORT;
+ return session_rt_engine_vft->table_add_del (srtg_handle, proto, args);
+}
+
+clib_error_t *
+session_rule_table_register_engine (const session_rt_engine_vft_t *vft);
+clib_error_t *
+session_rule_table_deregister_engine (const session_rt_engine_vft_t *vft);
+
+extern session_rules_table_t *srtg_handle_to_srt (u32 srtg_handle, u32 proto);
+extern session_rules_table_group_t *srtg_instance_alloc (session_table_t *st,
+ u32 n_proto);
+extern void srtg_instance_free (session_table_t *st);
+
#endif /* SRC_VNET_SESSION_SESSION_RULES_TABLE_H_ */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/vnet/session/session_sdl.c b/src/vnet/session/session_sdl.c
new file mode 100644
index 00000000000..45ab705fb62
--- /dev/null
+++ b/src/vnet/session/session_sdl.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/session/session.h>
+#include <vnet/ip/ip4_forward.h>
+#include <vnet/ip/ip6_forward.h>
+#include <vnet/session/session_rules_table.h>
+#include <vnet/session/session_sdl.h>
+
+VLIB_REGISTER_LOG_CLASS (session_sdl_log, static) = { .class_name = "session",
+ .subclass_name = "sdl" };
+
+#define log_debug(fmt, ...) \
+ vlib_log_debug (session_sdl_log.class, "%s: " fmt, __func__, __VA_ARGS__)
+#define log_warn(fmt, ...) \
+ vlib_log_warn (session_sdl_log.class, fmt, __VA_ARGS__)
+#define log_err(fmt, ...) \
+ vlib_log_err (session_sdl_log.class, fmt, __VA_ARGS__)
+
+static fib_source_t sdl_fib_src;
+static dpo_type_t sdl_dpo_type;
+
+const static char *const *const session_sdl_dpo_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = (const char *const[]){ "ip4-drop", 0 },
+ [DPO_PROTO_IP6] = (const char *const[]){ "ip6-drop", 0 },
+};
+
+static fib_route_path_t *
+session_sdl_fib_create_route_paths (u32 fib_index, dpo_proto_t dpo_proto)
+{
+ fib_route_path_t *paths = 0;
+ fib_route_path_t path = {
+ .frp_proto = dpo_proto,
+ .frp_flags = FIB_ROUTE_PATH_EXCLUSIVE,
+ .frp_fib_index = fib_index,
+ .frp_sw_if_index = ~0,
+ .frp_weight = 1,
+ };
+ vec_add1 (paths, path);
+ return paths;
+}
+
+static void
+session_sdl_dpo_lock (dpo_id_t *dpo)
+{
+}
+
+static void
+session_sdl_dpo_unlock (dpo_id_t *dpo)
+{
+}
+
+static u8 *
+format_session_sdl_dpo (u8 *s, va_list *va)
+{
+ index_t index = va_arg (*va, index_t);
+
+ return format (s, "sdl: [index: %u, deny]", index);
+}
+
+static const dpo_vft_t session_sdl_dpo_vft = {
+ .dv_lock = session_sdl_dpo_lock,
+ .dv_unlock = session_sdl_dpo_unlock,
+ .dv_format = format_session_sdl_dpo,
+};
+
+static u32
+session_sdl_lookup6 (u32 srtg_handle, u32 proto, ip6_address_t *lcl_ip,
+ ip6_address_t *rmt_ip, u16 lcl_port, u16 rmt_port)
+{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb = &srt->sdl_block;
+ index_t lbi;
+ const dpo_id_t *dpo;
+
+ if (sdlb->ip6_fib_index == ~0)
+ return SESSION_TABLE_INVALID_INDEX;
+ lbi = ip6_fib_table_fwding_lookup (sdlb->ip6_fib_index, lcl_ip);
+ dpo = load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
+ if (dpo->dpoi_type != sdl_dpo_type)
+ return SESSION_TABLE_INVALID_INDEX;
+ return (dpo->dpoi_index);
+}
+
+static u32
+session_sdl_lookup4 (u32 srtg_handle, u32 proto, ip4_address_t *lcl_ip,
+ ip4_address_t *rmt_ip, u16 lcl_port, u16 rmt_port)
+{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb = &srt->sdl_block;
+ index_t lbi;
+ const dpo_id_t *dpo;
+
+ if (sdlb->ip_fib_index == ~0)
+ return SESSION_TABLE_INVALID_INDEX;
+ lbi = ip4_fib_forwarding_lookup (sdlb->ip_fib_index, lcl_ip);
+ dpo = load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
+ if (dpo->dpoi_type != sdl_dpo_type)
+ return SESSION_TABLE_INVALID_INDEX;
+ return (dpo->dpoi_index);
+}
+
+typedef struct session_sdl4_fib_show_walk_ctx_t_
+{
+ fib_node_index_t *ifsw_indicies;
+} session_sdl4_fib_show_walk_ctx_t;
+
+static fib_table_walk_rc_t
+session_sdl4_fib_show_walk_cb (fib_node_index_t fei, void *arg)
+{
+ session_sdl4_fib_show_walk_ctx_t *ctx = arg;
+
+ vec_add1 (ctx->ifsw_indicies, fei);
+
+ return (FIB_TABLE_WALK_CONTINUE);
+}
+
+typedef struct session_sdl6_fib_show_ctx_t_
+{
+ fib_node_index_t *entries;
+} session_sdl6_fib_show_ctx_t;
+
+static fib_table_walk_rc_t
+session_sdl6_fib_table_show_walk (fib_node_index_t fei, void *arg)
+{
+ session_sdl6_fib_show_ctx_t *ctx = arg;
+
+ vec_add1 (ctx->entries, fei);
+
+ return (FIB_TABLE_WALK_CONTINUE);
+}
+
+static void
+session_sdl_fib_table_show (u32 fei, ip46_address_t *lcl_ip, u16 fp_len,
+ u32 action_index, u32 fp_proto, u8 *tag,
+ void *args)
+{
+ vlib_main_t *vm = args;
+ u32 type = (fp_proto == FIB_PROTOCOL_IP4) ? IP46_TYPE_IP4 : IP46_TYPE_IP6;
+
+ vlib_cli_output (vm, "[%d] rule: %U/%d action: %d tag %U", fei,
+ format_ip46_address, lcl_ip, type, fp_len, action_index,
+ format_session_rule_tag, tag);
+}
+
+static void
+session_sdl_cli_dump (vlib_main_t *vm, u32 srtg_handle, u32 proto,
+ u8 fib_proto)
+{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb = &srt->sdl_block;
+
+ if (fib_proto == FIB_PROTOCOL_IP4)
+ {
+ vlib_cli_output (vm, "IP4 rules, fib index %d", sdlb->ip_fib_index);
+ session_sdl_table_walk4 (srtg_handle, session_sdl_fib_table_show, vm);
+ }
+ else if (fib_proto == FIB_PROTOCOL_IP6)
+ {
+ vlib_cli_output (vm, "IP6 rules, fib index %d", sdlb->ip6_fib_index);
+ session_sdl_table_walk6 (srtg_handle, session_sdl_fib_table_show, vm);
+ }
+}
+
+static void
+session_sdl4_fib_table_show_one (session_rules_table_t *srt, u32 fib_index,
+ vlib_main_t *vm, ip4_address_t *address,
+ u32 mask_len)
+{
+ ip4_fib_t *fib;
+ fib_node_index_t fei;
+
+ fib = ip4_fib_get (fib_index);
+ fei = ip4_fib_table_lookup (fib, address, mask_len);
+ if (fei != FIB_NODE_INDEX_INVALID && fib_entry_is_sourced (fei, sdl_fib_src))
+ {
+ u8 *tag = session_rules_table_rule_tag (srt, fei, 1);
+ fib_entry_t *fib_entry = fib_entry_get (fei);
+ fib_prefix_t pfx = fib_entry->fe_prefix;
+ index_t lbi = ip4_fib_forwarding_lookup (fib_index, &pfx.fp_addr.ip4);
+ const dpo_id_t *dpo =
+ load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
+
+ session_sdl_fib_table_show (fei, &pfx.fp_addr, pfx.fp_len,
+ dpo->dpoi_index, FIB_PROTOCOL_IP4, tag, vm);
+ }
+}
+
+static void
+session_sdl6_fib_table_show_one (session_rules_table_t *srt, u32 fib_index,
+ vlib_main_t *vm, ip6_address_t *address,
+ u32 mask_len)
+{
+ fib_node_index_t fei;
+
+ fei = ip6_fib_table_lookup (fib_index, address, mask_len);
+ if (fei != FIB_NODE_INDEX_INVALID && fib_entry_is_sourced (fei, sdl_fib_src))
+ {
+ u8 *tag = session_rules_table_rule_tag (srt, fei, 0);
+ fib_entry_t *fib_entry = fib_entry_get (fei);
+ fib_prefix_t pfx = fib_entry->fe_prefix;
+ index_t lbi = ip6_fib_table_fwding_lookup (fib_index, &pfx.fp_addr.ip6);
+ const dpo_id_t *dpo =
+ load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
+
+ session_sdl_fib_table_show (fei, &pfx.fp_addr, pfx.fp_len,
+ dpo->dpoi_index, FIB_PROTOCOL_IP6, tag, vm);
+ }
+}
+
+static void
+session_sdl_show_rule (vlib_main_t *vm, u32 srtg_handle, u32 proto,
+ ip46_address_t *lcl_ip, u16 lcl_port,
+ ip46_address_t *rmt_ip, u16 rmt_port, u8 is_ip4)
+{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb;
+
+ sdlb = &srt->sdl_block;
+ if (is_ip4)
+ session_sdl4_fib_table_show_one (srt, sdlb->ip_fib_index, vm, &lcl_ip->ip4,
+ 32);
+ else
+ session_sdl6_fib_table_show_one (srt, sdlb->ip6_fib_index, vm,
+ &lcl_ip->ip6, 128);
+}
+
+static void
+session_sdl_table_init (session_table_t *st, u8 fib_proto)
+{
+ session_rules_table_t *srt;
+ session_sdl_block_t *sdlb;
+ u8 all = fib_proto > FIB_PROTOCOL_IP6 ? 1 : 0;
+ char name[80];
+ app_namespace_t *app_ns = app_namespace_get (st->appns_index);
+ session_rules_table_group_t *srtg;
+
+ /* Don't support local table */
+ if (st->is_local == 1)
+ return;
+
+ srtg = srtg_instance_alloc (st, 0);
+ srt = srtg->session_rules;
+ sdlb = &srt->sdl_block;
+
+ if (fib_proto == FIB_PROTOCOL_IP4 || all)
+ {
+ snprintf (name, sizeof (name), "sdl4 %s", app_ns->ns_id);
+ sdlb->ip_table_id = ip_table_get_unused_id (FIB_PROTOCOL_IP4);
+ sdlb->ip_fib_index = fib_table_find_or_create_and_lock_w_name (
+ FIB_PROTOCOL_IP4, sdlb->ip_table_id, sdl_fib_src, (const u8 *) name);
+ }
+
+ if (fib_proto == FIB_PROTOCOL_IP6 || all)
+ {
+ snprintf (name, sizeof (name), "sdl6 %s", app_ns->ns_id);
+ sdlb->ip6_table_id = ip_table_get_unused_id (FIB_PROTOCOL_IP6);
+ sdlb->ip6_fib_index = fib_table_find_or_create_and_lock_w_name (
+ FIB_PROTOCOL_IP6, sdlb->ip6_table_id, sdl_fib_src, (const u8 *) name);
+ }
+
+ srt->rules_by_tag = hash_create_vec (0, sizeof (u8), sizeof (uword));
+ srt->tags_by_rules = hash_create (0, sizeof (uword));
+}
+
+static void
+session_sdl_table_free (session_table_t *st, u8 fib_proto)
+{
+ session_rules_table_t *srt = srtg_handle_to_srt (st->srtg_handle, 0);
+ session_sdl_block_t *sdlb;
+ u8 all = fib_proto > FIB_PROTOCOL_IP6 ? 1 : 0;
+
+ ASSERT (st->is_local == 0);
+ sdlb = &srt->sdl_block;
+ if ((fib_proto == FIB_PROTOCOL_IP4 || all) && (sdlb->ip_fib_index != ~0))
+ {
+ fib_table_flush (sdlb->ip_fib_index, FIB_PROTOCOL_IP4, sdl_fib_src);
+ fib_table_unlock (sdlb->ip_fib_index, FIB_PROTOCOL_IP4, sdl_fib_src);
+ }
+ if ((fib_proto == FIB_PROTOCOL_IP6 || all) && (sdlb->ip6_fib_index != ~0))
+ {
+ fib_table_flush (sdlb->ip6_fib_index, FIB_PROTOCOL_IP6, sdl_fib_src);
+ fib_table_unlock (sdlb->ip6_fib_index, FIB_PROTOCOL_IP6, sdl_fib_src);
+ }
+
+ hash_free (srt->tags_by_rules);
+ hash_free (srt->rules_by_tag);
+
+ srtg_instance_free (st);
+}
+
+static session_error_t
+session_sdl_add_del (u32 srtg_handle, u32 proto,
+ session_rule_table_add_del_args_t *args)
+{
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb = &srt->sdl_block;
+ u32 fib_index;
+ dpo_proto_t dpo_proto;
+ fib_route_path_t *paths = 0;
+ fib_prefix_t pfx = args->lcl;
+ session_error_t err = SESSION_E_NONE;
+ fib_node_index_t fei;
+ int is_ip4;
+
+ if (!(args->lcl_port == 0 && args->rmt_port == 0 &&
+ args->rmt.fp_addr.ip4.as_u32 == 0))
+ return SESSION_E_NOSUPPORT;
+
+ fei = session_rules_table_rule_for_tag (srt, args->tag);
+ if (args->is_add && fei != SESSION_RULES_TABLE_INVALID_INDEX)
+ return SESSION_E_INVALID;
+
+ if (args->lcl.fp_proto == FIB_PROTOCOL_IP4)
+ {
+ fib_index = sdlb->ip_fib_index;
+ dpo_proto = DPO_PROTO_IP4;
+ is_ip4 = 1;
+ }
+ else
+ {
+ fib_index = sdlb->ip6_fib_index;
+ dpo_proto = DPO_PROTO_IP6;
+ is_ip4 = 0;
+ }
+
+ paths = session_sdl_fib_create_route_paths (fib_index, dpo_proto);
+ if (args->is_add)
+ {
+ fei = fib_table_lookup_exact_match (fib_index, &pfx);
+ if (fei != FIB_NODE_INDEX_INVALID)
+ {
+ err = SESSION_E_IPINUSE;
+ goto done;
+ }
+ dpo_set (&paths->dpo, sdl_dpo_type, dpo_proto, args->action_index);
+ fei = fib_table_entry_path_add2 (fib_index, &pfx, sdl_fib_src,
+ FIB_ENTRY_FLAG_EXCLUSIVE, paths);
+ session_rules_table_add_tag (srt, args->tag, fei, is_ip4);
+ dpo_reset (&paths->dpo);
+ }
+ else
+ {
+ if (fei == SESSION_RULES_TABLE_INVALID_INDEX)
+ {
+ fei = fib_table_lookup_exact_match (fib_index, &pfx);
+
+ if (fei == FIB_NODE_INDEX_INVALID)
+ {
+ err = SESSION_E_NOROUTE;
+ goto done;
+ }
+ }
+
+ if (!fib_entry_is_sourced (fei, sdl_fib_src))
+ {
+ err = SESSION_E_NOROUTE;
+ goto done;
+ }
+
+ fib_entry_t *fib_entry = fib_entry_get (fei);
+ pfx = fib_entry->fe_prefix;
+ fib_table_entry_special_remove (fib_index, &pfx, sdl_fib_src);
+ session_rules_table_del_tag (srt, args->tag, is_ip4);
+ }
+done:
+ vec_free (paths);
+
+ return err;
+}
+
+static const session_rt_engine_vft_t session_sdl_vft = {
+ .backend_engine = RT_BACKEND_ENGINE_SDL,
+ .table_lookup4 = session_sdl_lookup4,
+ .table_lookup6 = session_sdl_lookup6,
+ .table_cli_dump = session_sdl_cli_dump,
+ .table_show_rule = session_sdl_show_rule,
+ .table_add_del = session_sdl_add_del,
+ .table_init = session_sdl_table_init,
+ .table_free = session_sdl_table_free,
+};
+
+static void
+session_sdl_fib_init (void)
+{
+ static u32 session_fib_inited = 0;
+
+ if (session_fib_inited)
+ return;
+ session_fib_inited = 1;
+ sdl_fib_src = fib_source_allocate ("session sdl", FIB_SOURCE_PRIORITY_LOW,
+ FIB_SOURCE_BH_SIMPLE);
+ sdl_dpo_type =
+ dpo_register_new_type (&session_sdl_dpo_vft, session_sdl_dpo_nodes);
+}
+
+static void
+session_sdl_app_namespace_walk_cb (app_namespace_t *app_ns, void *ctx)
+{
+ u32 fib_index, table_index;
+ session_table_t *st;
+
+ log_debug ("disable app_ns %s", app_ns->ns_id);
+
+ fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP4);
+ table_index = session_lookup_get_index_for_fib (FIB_PROTOCOL_IP4, fib_index);
+ st = session_table_get (table_index);
+ if (st)
+ session_rules_table_free (st, FIB_PROTOCOL_IP4);
+
+ fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP6);
+ table_index = session_lookup_get_index_for_fib (FIB_PROTOCOL_IP6, fib_index);
+ st = session_table_get (table_index);
+ if (st)
+ session_rules_table_free (st, FIB_PROTOCOL_IP6);
+}
+
+clib_error_t *
+session_sdl_enable_disable (int enable)
+{
+ clib_error_t *error = 0;
+
+ if (enable)
+ {
+ error = session_rule_table_register_engine (&session_sdl_vft);
+ if (error)
+ {
+ log_err ("error in enabling sdl: %U", format_clib_error, error);
+ return error;
+ }
+ session_sdl_fib_init ();
+ }
+ else
+ {
+ app_namespace_walk (session_sdl_app_namespace_walk_cb, 0);
+
+ error = session_rule_table_deregister_engine (&session_sdl_vft);
+ if (error)
+ log_err ("error in disabling sdl: %U", format_clib_error, error);
+ }
+
+ return error;
+}
+
+/*
+ * Source Deny List
+ */
+static clib_error_t *
+session_sdl_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ u32 appns_index;
+ app_namespace_t *app_ns;
+ u32 lcl_plen = 0, action = 0;
+ clib_error_t *error = 0;
+ ip46_address_t lcl_ip;
+ u8 conn_set = 0;
+ u8 fib_proto = -1, is_add = 1, *ns_id = 0;
+ u8 *tag = 0, tag_only = 0;
+ int rv;
+ session_rule_add_del_args_t args;
+
+ session_cli_return_if_not_enabled ();
+
+ if (session_sdl_is_enabled () == 0)
+ return clib_error_return (0, "session sdl engine is not enabled");
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "del"))
+ is_add = 0;
+ else if (unformat (input, "add"))
+ ;
+ else if (unformat (input, "appns %_%v%_", &ns_id))
+ ;
+ else if (unformat (input, "%U/%d", unformat_ip4_address, &lcl_ip.ip4,
+ &lcl_plen))
+ {
+ fib_proto = FIB_PROTOCOL_IP4;
+ conn_set = 1;
+ }
+ else if (unformat (input, "%U/%d", unformat_ip6_address, &lcl_ip.ip6,
+ &lcl_plen))
+ {
+ fib_proto = FIB_PROTOCOL_IP6;
+ conn_set = 1;
+ }
+ else if (unformat (input, "action %d", &action))
+ ;
+ else if (unformat (input, "tag %_%v%_", &tag))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ goto done;
+ }
+ }
+
+ if (ns_id)
+ {
+ app_ns = app_namespace_get_from_id (ns_id);
+ if (!app_ns)
+ {
+ vlib_cli_output (vm, "namespace %v does not exist", ns_id);
+ goto done;
+ }
+ }
+ else
+ {
+ app_ns = app_namespace_get_default ();
+ }
+ appns_index = app_namespace_index (app_ns);
+
+ if (is_add && !conn_set && action == 0)
+ {
+ vlib_cli_output (vm, "connection and action must be set for add");
+ goto done;
+ }
+ if (!is_add && !tag && !conn_set)
+ {
+ vlib_cli_output (vm, "connection or tag must be set for delete");
+ goto done;
+ }
+ if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
+ {
+ vlib_cli_output (vm, "tag too long (max u64)");
+ goto done;
+ }
+
+ /* Delete with only tag entered. Try v4 first and then v6 if failed */
+ if ((is_add == 0) && (fib_proto == (u8) ~0))
+ {
+ fib_proto = FIB_PROTOCOL_IP4;
+ tag_only = 1;
+ }
+
+ memset (&args, 0, sizeof (args));
+ args.transport_proto = TRANSPORT_PROTO_TCP;
+ args.table_args.lcl.fp_addr = lcl_ip;
+ args.table_args.lcl.fp_len = lcl_plen;
+ args.table_args.lcl.fp_proto = fib_proto;
+ args.table_args.rmt.fp_proto = fib_proto;
+ args.table_args.action_index = action;
+ args.table_args.is_add = is_add;
+ args.table_args.tag = tag;
+ args.appns_index = appns_index;
+ args.scope = SESSION_RULE_SCOPE_GLOBAL;
+
+ if ((rv = vnet_session_rule_add_del (&args)))
+ {
+ /* Try tag only delete on v6 */
+ if (rv && tag_only)
+ {
+ args.table_args.rmt.fp_proto = FIB_PROTOCOL_IP6;
+ args.table_args.lcl.fp_proto = FIB_PROTOCOL_IP6;
+ if ((rv = vnet_session_rule_add_del (&args)))
+ {
+ error = clib_error_return (0, "sdl add del returned %u", rv);
+ }
+ }
+ else
+ {
+ error = clib_error_return (0, "sdl add del returned %u", rv);
+ }
+ }
+
+done:
+ vec_free (ns_id);
+ vec_free (tag);
+ return error;
+}
+
+VLIB_CLI_COMMAND (session_sdl_command, static) = {
+ .path = "session sdl",
+ .short_help = "session sdl [add|del] [appns <ns_id>] <lcl-ip/plen> action "
+ "<action> [tag <tag>]",
+ .function = session_sdl_command_fn,
+ .is_mp_safe = 1,
+};
+
+static clib_error_t *
+show_session_sdl_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ u32 fib_index;
+ ip46_address_t lcl_ip;
+ u8 show_one = 0;
+ app_namespace_t *app_ns;
+ session_table_t *st;
+ u8 *ns_id = 0, fib_proto = FIB_PROTOCOL_IP4;
+
+ session_cli_return_if_not_enabled ();
+
+ clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "appns %_%s%_", &ns_id))
+ ;
+ else if (unformat (input, "%U", unformat_ip4_address, &lcl_ip.ip4))
+ {
+ fib_proto = FIB_PROTOCOL_IP4;
+ show_one = 1;
+ }
+ else if (unformat (input, "%U", unformat_ip6_address, &lcl_ip.ip6))
+ {
+ fib_proto = FIB_PROTOCOL_IP6;
+ show_one = 1;
+ }
+ else
+ {
+ vec_free (ns_id);
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ }
+
+ if (ns_id)
+ {
+ app_ns = app_namespace_get_from_id (ns_id);
+ if (!app_ns)
+ {
+ vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
+ goto done;
+ }
+ }
+ else
+ {
+ app_ns = app_namespace_get_default ();
+ }
+
+ if (session_sdl_is_enabled () == 0)
+ {
+ vlib_cli_output (vm, "session sdl engine is not enabled");
+ goto done;
+ }
+
+ if (show_one)
+ {
+ fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
+ st = session_table_get_for_fib_index (fib_proto, fib_index);
+ if (st && (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID))
+ session_rules_table_show_rule (vm, st->srtg_handle, 0, &lcl_ip, 0, 0,
+ 0, (fib_proto == FIB_PROTOCOL_IP4));
+ goto done;
+ }
+
+ /* 2 separate session tables for global entries, 1 for ip4 and 1 for ip6 */
+ fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP4);
+ st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
+ if (st && (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID))
+ session_rules_table_cli_dump (vm, st->srtg_handle, 0, FIB_PROTOCOL_IP4);
+
+ fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP6);
+ st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
+ if (st && (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID))
+ session_rules_table_cli_dump (vm, st->srtg_handle, 0, FIB_PROTOCOL_IP6);
+done:
+ vec_free (ns_id);
+ return 0;
+}
+
+void
+session_sdl_table_walk4 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
+ void *args)
+{
+ ip4_fib_t *fib;
+ session_sdl4_fib_show_walk_ctx_t ctx = {
+ .ifsw_indicies = NULL,
+ };
+ fib_node_index_t *fei;
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb = &srt->sdl_block;
+ u32 fib_index = sdlb->ip_fib_index;
+
+ if (fib_index == ~0)
+ return;
+ fib = ip4_fib_get (fib_index);
+ ip4_fib_table_walk (fib, session_sdl4_fib_show_walk_cb, &ctx);
+ vec_sort_with_function (ctx.ifsw_indicies, fib_entry_cmp_for_sort);
+
+ vec_foreach (fei, ctx.ifsw_indicies)
+ {
+ if (*fei != FIB_NODE_INDEX_INVALID &&
+ fib_entry_is_sourced (*fei, sdl_fib_src))
+ {
+ u8 *tag = session_rules_table_rule_tag (srt, *fei, 1);
+ fib_entry_t *fib_entry = fib_entry_get (*fei);
+ fib_prefix_t pfx = fib_entry->fe_prefix;
+ index_t lbi =
+ ip4_fib_forwarding_lookup (fib_index, &pfx.fp_addr.ip4);
+ const dpo_id_t *dpo =
+ load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
+
+ fn (*fei, &pfx.fp_addr, pfx.fp_len, dpo->dpoi_index,
+ FIB_PROTOCOL_IP4, tag, args);
+ }
+ }
+
+ vec_free (ctx.ifsw_indicies);
+}
+
+void
+session_sdl_table_walk6 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
+ void *args)
+{
+ ip6_fib_t *fib;
+ fib_node_index_t *fei;
+ session_sdl6_fib_show_ctx_t ctx = {
+ .entries = NULL,
+ };
+ session_rules_table_t *srt = srtg_handle_to_srt (srtg_handle, 0);
+ session_sdl_block_t *sdlb = &srt->sdl_block;
+ u32 fib_index = sdlb->ip6_fib_index;
+
+ if (fib_index == ~0)
+ return;
+ fib = ip6_fib_get (fib_index);
+ ip6_fib_table_walk (fib->index, session_sdl6_fib_table_show_walk, &ctx);
+ vec_sort_with_function (ctx.entries, fib_entry_cmp_for_sort);
+
+ vec_foreach (fei, ctx.entries)
+ {
+ if (*fei != FIB_NODE_INDEX_INVALID &&
+ fib_entry_is_sourced (*fei, sdl_fib_src))
+ {
+ u8 *tag = session_rules_table_rule_tag (srt, *fei, 0);
+ fib_entry_t *fib_entry = fib_entry_get (*fei);
+ fib_prefix_t pfx = fib_entry->fe_prefix;
+ index_t lbi =
+ ip6_fib_table_fwding_lookup (fib_index, &pfx.fp_addr.ip6);
+ const dpo_id_t *dpo =
+ load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
+
+ fn (*fei, &pfx.fp_addr, pfx.fp_len, dpo->dpoi_index,
+ FIB_PROTOCOL_IP6, tag, args);
+ }
+ }
+
+ vec_free (ctx.entries);
+}
+
+VLIB_CLI_COMMAND (show_session_sdl_command, static) = {
+ .path = "show session sdl",
+ .short_help = "show session sdl [appns <id> <lcl-ip>]",
+ .function = show_session_sdl_command_fn,
+ .is_mp_safe = 1,
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/session/session_sdl.h b/src/vnet/session/session_sdl.h
new file mode 100644
index 00000000000..8d8b5b2d29e
--- /dev/null
+++ b/src/vnet/session/session_sdl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_VNET_SESSION_SESSION_SDL_H_
+#define SRC_VNET_SESSION_SESSION_SDL_H_
+
+clib_error_t *session_sdl_enable_disable (int enable);
+
+typedef void (*session_sdl_table_walk_fn_t) (u32 fei, ip46_address_t *lcl_ip,
+ u16 fp_len, u32 action_index,
+ u32 fb_proto, u8 *tag, void *ctx);
+void session_sdl_table_walk4 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
+ void *args);
+void session_sdl_table_walk6 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
+ void *args);
+
+#endif /* SRC_VNET_SESSION_SESSION_SDL_H_ */
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/session/session_table.c b/src/vnet/session/session_table.c
index dbbe771979c..5dafe0e633c 100644
--- a/src/vnet/session/session_table.c
+++ b/src/vnet/session/session_table.c
@@ -15,6 +15,7 @@
#include <vnet/session/session_table.h>
#include <vnet/session/session.h>
+#include <vnet/session/session_rules_table.h>
/**
* Pool of session tables
@@ -64,12 +65,8 @@ void
session_table_free (session_table_t *slt, u8 fib_proto)
{
u8 all = fib_proto > FIB_PROTOCOL_IP6 ? 1 : 0;
- int i;
- for (i = 0; i < TRANSPORT_N_PROTOS; i++)
- session_rules_table_free (&slt->session_rules[i]);
-
- vec_free (slt->session_rules);
+ session_rules_table_free (slt, fib_proto);
if (fib_proto == FIB_PROTOCOL_IP4 || all)
{
@@ -92,10 +89,9 @@ session_table_free (session_table_t *slt, u8 fib_proto)
* otherwise it uses defaults above.
*/
void
-session_table_init (session_table_t * slt, u8 fib_proto)
+session_table_init (session_table_t *slt, u8 fib_proto)
{
u8 all = fib_proto > FIB_PROTOCOL_IP6 ? 1 : 0;
- int i;
#define _(af,table,parm,value) \
u32 configured_##af##_##table##_table_##parm = value;
@@ -109,6 +105,7 @@ session_table_init (session_table_t * slt, u8 fib_proto)
foreach_hash_table_parameter;
#undef _
+ slt->srtg_handle = SESSION_SRTG_HANDLE_INVALID;
if (fib_proto == FIB_PROTOCOL_IP4 || all)
{
clib_bihash_init2_args_16_8_t _a, *a = &_a;
@@ -153,10 +150,6 @@ session_table_init (session_table_t * slt, u8 fib_proto)
a->instantiate_immediately = 1;
clib_bihash_init2_48_8 (a);
}
-
- vec_validate (slt->session_rules, TRANSPORT_N_PROTOS - 1);
- for (i = 0; i < TRANSPORT_N_PROTOS; i++)
- session_rules_table_init (&slt->session_rules[i]);
}
typedef struct _ip4_session_table_walk_ctx_t
diff --git a/src/vnet/session/session_table.h b/src/vnet/session/session_table.h
index 636b8d77bee..aae4a1c2af5 100644
--- a/src/vnet/session/session_table.h
+++ b/src/vnet/session/session_table.h
@@ -18,7 +18,6 @@
#include <vppinfra/bihash_16_8.h>
#include <vppinfra/bihash_48_8.h>
-#include <vnet/session/session_rules_table.h>
typedef struct _session_lookup_table
{
@@ -37,7 +36,7 @@ typedef struct _session_lookup_table
/**
* Per fib proto and transport proto session rules tables
*/
- session_rules_table_t *session_rules;
+ u32 srtg_handle;
/** Flag that indicates if table has local scope */
u8 is_local;
@@ -78,6 +77,8 @@ session_table_t *_get_session_tables ();
#define session_table_foreach(VAR, BODY) \
pool_foreach (VAR, _get_session_tables ()) BODY
+void session_lookup_table_cleanup (u32 fib_proto, u32 fib_index);
+
#endif /* SRC_VNET_SESSION_SESSION_TABLE_H_ */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/vnet/session/session_test.c b/src/vnet/session/session_test.c
index 770e7263024..1c865b877b5 100644
--- a/src/vnet/session/session_test.c
+++ b/src/vnet/session/session_test.c
@@ -271,6 +271,12 @@ api_session_enable_disable (vat_main_t *vat)
}
static int
+api_session_enable_disable_v2 (vat_main_t *vat)
+{
+ return -1;
+}
+
+static int
api_app_worker_add_del (vat_main_t *vat)
{
return -1;
@@ -354,6 +360,23 @@ api_session_sapi_enable_disable (vat_main_t *vat)
return -1;
}
+static int
+api_session_sdl_add_del (vat_main_t *vam)
+{
+ return -1;
+}
+
+static void
+vl_api_session_sdl_details_t_handler (vl_api_session_rules_details_t *mp)
+{
+}
+
+static int
+api_session_sdl_dump (vat_main_t *vam)
+{
+ return -1;
+}
+
#include <vnet/session/session.api_test.c>
/*
diff --git a/src/vnet/snap/snap.c b/src/vnet/snap/snap.c
index 9bee415390a..bf5994271a4 100644
--- a/src/vnet/snap/snap.c
+++ b/src/vnet/snap/snap.c
@@ -192,8 +192,9 @@ snap_init (vlib_main_t * vm)
return vlib_call_init_function (vm, snap_input_init);
}
-VLIB_INIT_FUNCTION (snap_init);
-
+VLIB_INIT_FUNCTION (snap_init) = {
+ .runs_after = VLIB_INITS ("llc_init"),
+};
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/vnet/srv6/sr_localsid.c b/src/vnet/srv6/sr_localsid.c
index 12349bb95e8..62b1a271576 100644
--- a/src/vnet/srv6/sr_localsid.c
+++ b/src/vnet/srv6/sr_localsid.c
@@ -1471,6 +1471,7 @@ sr_localsid_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
ip6_header_t *ip0, *ip1, *ip2, *ip3;
ip6_sr_header_t *sr0, *sr1, *sr2, *sr3;
ip6_ext_header_t *prev0, *prev1, *prev2, *prev3;
+ prev0 = prev1 = prev2 = prev3 = 0;
u32 next0, next1, next2, next3;
next0 = next1 = next2 = next3 = SR_LOCALSID_NEXT_IP6_LOOKUP;
ip6_sr_localsid_t *ls0, *ls1, *ls2, *ls3;
@@ -1779,6 +1780,7 @@ sr_localsid_un_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
ip6_header_t *ip0, *ip1, *ip2, *ip3;
ip6_sr_header_t *sr0, *sr1, *sr2, *sr3;
ip6_ext_header_t *prev0, *prev1, *prev2, *prev3;
+ prev0 = prev1 = prev2 = prev3 = 0;
u32 next0, next1, next2, next3;
next0 = next1 = next2 = next3 = SR_LOCALSID_NEXT_IP6_LOOKUP;
ip6_sr_localsid_t *ls0, *ls1, *ls2, *ls3;
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index efc72a227e8..1afc07918b7 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -879,6 +879,8 @@ format_tcp_listener_session (u8 * s, va_list * args)
if (verbose)
s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_tcp_state,
tc->state);
+ if (verbose == 2)
+ s = format (s, "\n%U", format_tcp_listener_connection, tc);
return s;
}
@@ -1512,6 +1514,10 @@ tcp_main_enable (vlib_main_t * vm)
clib_error_t *error = 0;
int thread;
+ /* Already initialized */
+ if (tm->wrk_ctx)
+ return 0;
+
if ((error = vlib_call_init_function (vm, ip_main_init)))
return error;
if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
@@ -1642,6 +1648,9 @@ tcp_configuration_init (void)
/* This value is seconds */
tcp_cfg.cleanup_time = 0.1; /* 100ms */
+
+ /* Time constants defined as tcp tick (1us) multiples */
+ tcp_cfg.syn_rcvd_time = TCP_ESTABLISH_TIME;
}
static clib_error_t *
diff --git a/src/vnet/tcp/tcp.h b/src/vnet/tcp/tcp.h
index 2362a8bb857..8676db413a0 100644
--- a/src/vnet/tcp/tcp.h
+++ b/src/vnet/tcp/tcp.h
@@ -45,19 +45,21 @@ typedef struct _tcp_lookup_dispatch
u8 next, error;
} tcp_lookup_dispatch_t;
-#define foreach_tcp_wrk_stat \
- _(timer_expirations, u64, "timer expirations") \
- _(rxt_segs, u64, "segments retransmitted") \
- _(tr_events, u32, "timer retransmit events") \
- _(to_closewait, u32, "timeout close-wait") \
- _(to_closewait2, u32, "timeout close-wait w/data") \
- _(to_finwait1, u32, "timeout fin-wait-1") \
- _(to_finwait2, u32, "timeout fin-wait-2") \
- _(to_lastack, u32, "timeout last-ack") \
- _(to_closing, u32, "timeout closing") \
- _(tr_abort, u32, "timer retransmit abort") \
- _(rst_unread, u32, "reset on close due to unread data") \
- _(no_buffer, u32, "out of buffers") \
+#define foreach_tcp_wrk_stat \
+ _ (timer_expirations, u64, "timer expirations") \
+ _ (rxt_segs, u64, "segments retransmitted") \
+ _ (tr_events, u32, "timer retransmit events") \
+ _ (to_establish, u32, "timeout establish") \
+ _ (to_persist, u32, "timeout persist") \
+ _ (to_closewait, u32, "timeout close-wait") \
+ _ (to_closewait2, u32, "timeout close-wait w/data") \
+ _ (to_finwait1, u32, "timeout fin-wait-1") \
+ _ (to_finwait2, u32, "timeout fin-wait-2") \
+ _ (to_lastack, u32, "timeout last-ack") \
+ _ (to_closing, u32, "timeout closing") \
+ _ (tr_abort, u32, "timer retransmit abort") \
+ _ (rst_unread, u32, "reset on close due to unread data") \
+ _ (no_buffer, u32, "out of buffers")
typedef struct tcp_wrk_stats_
{
@@ -197,6 +199,9 @@ typedef struct tcp_configuration_
/** Time to wait (sec) before cleaning up the connection */
f32 cleanup_time;
+ /** Time to wait (tcp ticks) for syn-rcvd connection to establish */
+ u32 syn_rcvd_time;
+
/** Number of preallocated connections */
u32 preallocated_connections;
@@ -354,6 +359,7 @@ format_function_t format_tcp_flags;
format_function_t format_tcp_sacks;
format_function_t format_tcp_rcv_sacks;
format_function_t format_tcp_connection;
+format_function_t format_tcp_listener_connection;
format_function_t format_tcp_connection_id;
#define tcp_validate_txf_size(_tc, _a) \
diff --git a/src/vnet/tcp/tcp_cli.c b/src/vnet/tcp/tcp_cli.c
index b04c0bdc0cf..55bc5764df2 100644
--- a/src/vnet/tcp/tcp_cli.c
+++ b/src/vnet/tcp/tcp_cli.c
@@ -250,6 +250,21 @@ format_tcp_connection_id (u8 * s, va_list * args)
}
u8 *
+format_tcp_listener_connection (u8 *s, va_list *args)
+{
+ tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
+
+ s = format (s, " index: %u cfg_flags: %U cong_algo: %s snd_mss: %u\n",
+ tc->c_c_index, format_tcp_cfg_flags, tc, tc->cc_algo->name,
+ tc->snd_mss);
+ s = format (s, " next_node %u opaque 0x%x fib_index %u sw_if_index %d",
+ tc->next_node_index, tc->next_node_opaque, tc->c_fib_index,
+ tc->sw_if_index);
+
+ return s;
+}
+
+u8 *
format_tcp_connection (u8 * s, va_list * args)
{
tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
@@ -825,6 +840,74 @@ VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
.function = show_tcp_punt_fn,
};
+static u8 *
+format_tcp_cfg (u8 *s, va_list *args)
+{
+ tcp_configuration_t tm_cfg = va_arg (*args, tcp_configuration_t);
+
+ s = format (s, "max rx fifo size: %U\n", format_memory_size,
+ tm_cfg.max_rx_fifo);
+ s = format (s, "min rx fifo size: %U\n", format_memory_size,
+ tm_cfg.min_rx_fifo);
+ s = format (s, "default mtu: %u\n", tm_cfg.default_mtu);
+ s = format (s, "initial cwnd multiplier: %u\n",
+ tm_cfg.initial_cwnd_multiplier);
+ s = format (s, "tx pacing: %s\n",
+ tm_cfg.enable_tx_pacing ? "enabled" : "disabled");
+ s = format (s, "tso: %s\n", tm_cfg.allow_tso ? "allowed" : "disallowed");
+ s = format (s, "checksum offload: %s\n",
+ tm_cfg.csum_offload ? "enabled" : "disabled");
+ s = format (s, "congestion control algorithm: %s\n",
+ tcp_cc_algo_get (tm_cfg.cc_algo)->name);
+ s = format (s, "min rwnd update ack: %u\n", tm_cfg.rwnd_min_update_ack);
+ s = format (s, "max gso packet size: %U\n", format_memory_size,
+ tm_cfg.max_gso_size);
+ s = format (s, "close_wait time: %u sec\n",
+ (u32) (tm_cfg.closewait_time * TCP_TIMER_TICK));
+ s = format (s, "time_wait time: %u sec\n",
+ (u32) (tm_cfg.timewait_time * TCP_TIMER_TICK));
+ s = format (s, "fin_wait1 time: %u sec\n",
+ (u32) (tm_cfg.finwait1_time * TCP_TIMER_TICK));
+ s = format (s, "fin_wait2 time: %u sec\n",
+ (u32) (tm_cfg.finwait2_time * TCP_TIMER_TICK));
+ s = format (s, "last_ack time: %u sec\n",
+ (u32) (tm_cfg.lastack_time * TCP_TIMER_TICK));
+ s = format (s, "fin_ack time: %u sec\n",
+ (u32) (tm_cfg.closing_time * TCP_TIMER_TICK));
+ s = format (s, "syn_rcvd time: %u sec\n",
+ (u32) (tm_cfg.syn_rcvd_time * TCP_TICK));
+ s = format (s, "tcp allocation error cleanup time: %0.2f sec\n",
+ (f32) (tm_cfg.alloc_err_timeout * TCP_TIMER_TICK));
+ s = format (s, "connection cleanup time: %.2f sec\n", tm_cfg.cleanup_time);
+ s = format (s, "tcp preallocated connections: %u",
+ tm_cfg.preallocated_connections);
+
+ return s;
+}
+
+static clib_error_t *
+show_tcp_cfg_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ tcp_main_t *tm = vnet_get_tcp_main ();
+
+ if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+ input);
+ vlib_cli_output (vm, "-----------");
+ vlib_cli_output (vm, "tcp config");
+ vlib_cli_output (vm, "-----------");
+ vlib_cli_output (vm, "%U\n", format_tcp_cfg, tm->cfg);
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (show_tcp_cfg_command, static) = {
+ .path = "show tcp config",
+ .short_help = "show tcp config",
+ .function = show_tcp_cfg_fn,
+};
+
static clib_error_t *
show_tcp_stats_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
@@ -1009,6 +1092,8 @@ tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
tcp_cfg.alloc_err_timeout = tmp_time / TCP_TIMER_TICK;
else if (unformat (input, "cleanup-time %u", &tmp_time))
tcp_cfg.cleanup_time = tmp_time / 1000.0;
+ else if (unformat (input, "syn-rcvd-time %u", &tmp_time))
+ tcp_cfg.syn_rcvd_time = tmp_time * THZ;
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
diff --git a/src/vnet/tcp/tcp_output.c b/src/vnet/tcp/tcp_output.c
index 78148cd5695..dd1ec555902 100644
--- a/src/vnet/tcp/tcp_output.c
+++ b/src/vnet/tcp/tcp_output.c
@@ -1391,7 +1391,7 @@ tcp_timer_retransmit_handler (tcp_connection_t * tc)
tc->rtt_ts = 0;
/* Passive open establish timeout */
- if (tc->rto > TCP_ESTABLISH_TIME >> 1)
+ if (tc->rto > tcp_cfg.syn_rcvd_time >> 1)
{
tcp_connection_set_state (tc, TCP_STATE_CLOSED);
tcp_connection_timers_reset (tc);
@@ -1458,6 +1458,8 @@ tcp_timer_retransmit_syn_handler (tcp_connection_t * tc)
TCP_EVT (TCP_EVT_CC_EVT, tc, 2);
tc->rtt_ts = 0;
+ tcp_worker_stats_inc (wrk, to_establish, 1);
+
/* Active open establish timeout */
if (tc->rto >= TCP_ESTABLISH_TIME >> 1)
{
@@ -1507,6 +1509,8 @@ tcp_timer_persist_handler (tcp_connection_t * tc)
int n_bytes = 0;
u8 *data;
+ tcp_worker_stats_inc (wrk, to_persist, 1);
+
/* Problem already solved or worse */
if (tc->state == TCP_STATE_CLOSED || tc->snd_wnd > tc->snd_mss
|| (tc->flags & TCP_CONN_FINSNT))
diff --git a/src/vnet/tcp/tcp_timer.c b/src/vnet/tcp/tcp_timer.c
index d98d0d14b17..4d1c0624fee 100644
--- a/src/vnet/tcp/tcp_timer.c
+++ b/src/vnet/tcp/tcp_timer.c
@@ -20,6 +20,7 @@ void
tcp_timer_initialize_wheel (tcp_timer_wheel_t * tw,
void (*expired_timer_cb) (u32 *), f64 now)
{
+ ASSERT (tw->timers == 0);
tw_timer_wheel_init_tcp_twsl (tw, expired_timer_cb, TCP_TIMER_TICK, ~0);
tw->last_run_time = now;
}
diff --git a/src/vnet/tls/tls.c b/src/vnet/tls/tls.c
index 5f00e6e302d..9ca3a91af29 100644
--- a/src/vnet/tls/tls.c
+++ b/src/vnet/tls/tls.c
@@ -16,13 +16,10 @@
#include <vnet/session/application_interface.h>
#include <vppinfra/lock.h>
#include <vnet/tls/tls.h>
+#include <vnet/tls/tls_inlines.h>
static tls_main_t tls_main;
-static tls_engine_vft_t *tls_vfts;
-
-#define TLS_INVALID_HANDLE ~0
-#define TLS_IDX_MASK 0x00FFFFFF
-#define TLS_ENGINE_TYPE_SHIFT 28
+tls_engine_vft_t *tls_vfts;
void tls_disconnect (u32 ctx_handle, u32 thread_index);
@@ -31,7 +28,7 @@ tls_disconnect_transport (tls_ctx_t * ctx)
{
vnet_disconnect_args_t a = {
.handle = ctx->tls_session_handle,
- .app_index = tls_main.app_index,
+ .app_index = ctx->ts_app_index,
};
if (vnet_disconnect_session (&a))
@@ -50,6 +47,21 @@ tls_get_available_engine (void)
return CRYPTO_ENGINE_NONE;
}
+static crypto_engine_type_t
+tls_get_engine_type (crypto_engine_type_t requested,
+ crypto_engine_type_t preferred)
+{
+ if (requested != CRYPTO_ENGINE_NONE)
+ {
+ if (tls_vfts[requested].ctx_alloc)
+ return requested;
+ return CRYPTO_ENGINE_NONE;
+ }
+ if (!tls_vfts[preferred].ctx_alloc)
+ return tls_get_available_engine ();
+ return preferred;
+}
+
int
tls_add_vpp_q_rx_evt (session_t * s)
{
@@ -295,140 +307,6 @@ send_reply:
ctx->parent_app_api_context);
}
-static inline void
-tls_ctx_parse_handle (u32 ctx_handle, u32 * ctx_index, u32 * engine_type)
-{
- *ctx_index = ctx_handle & TLS_IDX_MASK;
- *engine_type = ctx_handle >> TLS_ENGINE_TYPE_SHIFT;
-}
-
-static inline crypto_engine_type_t
-tls_get_engine_type (crypto_engine_type_t requested,
- crypto_engine_type_t preferred)
-{
- if (requested != CRYPTO_ENGINE_NONE)
- {
- if (tls_vfts[requested].ctx_alloc)
- return requested;
- return CRYPTO_ENGINE_NONE;
- }
- if (!tls_vfts[preferred].ctx_alloc)
- return tls_get_available_engine ();
- return preferred;
-}
-
-static inline u32
-tls_ctx_alloc (crypto_engine_type_t engine_type)
-{
- u32 ctx_index;
- ctx_index = tls_vfts[engine_type].ctx_alloc ();
- return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
-}
-
-static inline u32
-tls_ctx_alloc_w_thread (crypto_engine_type_t engine_type, u32 thread_index)
-{
- u32 ctx_index;
- ctx_index = tls_vfts[engine_type].ctx_alloc_w_thread (thread_index);
- return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
-}
-
-static inline u32
-tls_ctx_attach (crypto_engine_type_t engine_type, u32 thread_index, void *ctx)
-{
- u32 ctx_index;
- ctx_index = tls_vfts[engine_type].ctx_attach (thread_index, ctx);
- return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
-}
-
-static inline void *
-tls_ctx_detach (tls_ctx_t *ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_detach (ctx);
-}
-
-static inline tls_ctx_t *
-tls_ctx_get (u32 ctx_handle)
-{
- u32 ctx_index, engine_type;
- tls_ctx_parse_handle (ctx_handle, &ctx_index, &engine_type);
- return tls_vfts[engine_type].ctx_get (ctx_index);
-}
-
-static inline tls_ctx_t *
-tls_ctx_get_w_thread (u32 ctx_handle, u8 thread_index)
-{
- u32 ctx_index, engine_type;
- tls_ctx_parse_handle (ctx_handle, &ctx_index, &engine_type);
- return tls_vfts[engine_type].ctx_get_w_thread (ctx_index, thread_index);
-}
-
-static inline int
-tls_ctx_init_server (tls_ctx_t * ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_init_server (ctx);
-}
-
-static inline int
-tls_ctx_init_client (tls_ctx_t * ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_init_client (ctx);
-}
-
-static inline int
-tls_ctx_write (tls_ctx_t * ctx, session_t * app_session,
- transport_send_params_t * sp)
-{
- u32 n_wrote;
-
- sp->max_burst_size = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
- n_wrote = tls_vfts[ctx->tls_ctx_engine].ctx_write (ctx, app_session, sp);
- sp->bytes_dequeued = n_wrote;
- return n_wrote > 0 ? clib_max (n_wrote / TRANSPORT_PACER_MIN_MSS, 1) : 0;
-}
-
-static inline int
-tls_ctx_read (tls_ctx_t * ctx, session_t * tls_session)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_read (ctx, tls_session);
-}
-
-static inline int
-tls_ctx_transport_close (tls_ctx_t * ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_transport_close (ctx);
-}
-
-static inline int
-tls_ctx_transport_reset (tls_ctx_t *ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_transport_reset (ctx);
-}
-
-static inline int
-tls_ctx_app_close (tls_ctx_t * ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_app_close (ctx);
-}
-
-void
-tls_ctx_free (tls_ctx_t * ctx)
-{
- tls_vfts[ctx->tls_ctx_engine].ctx_free (ctx);
-}
-
-u8
-tls_ctx_handshake_is_over (tls_ctx_t * ctx)
-{
- return tls_vfts[ctx->tls_ctx_engine].ctx_handshake_is_over (ctx);
-}
-
-int
-tls_reinit_ca_chain (crypto_engine_type_t tls_engine_id)
-{
- return tls_vfts[tls_engine_id].ctx_reinit_cachain ();
-}
-
void
tls_notify_app_io_error (tls_ctx_t *ctx)
{
@@ -771,6 +649,7 @@ tls_connect (transport_endpoint_cfg_t * tep)
ctx = tls_ctx_half_open_get (ctx_index);
ctx->parent_app_wrk_index = sep->app_wrk_index;
ctx->parent_app_api_context = sep->opaque;
+ ctx->ts_app_index = tm->app_index;
ctx->tcp_is_ip4 = sep->is_ip4;
ctx->tls_type = sep->transport_proto;
ctx->ckpair_index = ccfg->ckpair_index;
@@ -870,6 +749,7 @@ tls_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
lctx = tls_listener_ctx_get (lctx_index);
lctx->parent_app_wrk_index = sep->app_wrk_index;
+ lctx->ts_app_index = tm->app_index;
lctx->tls_session_handle = tls_al_handle;
lctx->app_session_handle = listen_session_get_handle (app_listener);
lctx->tcp_is_ip4 = sep->is_ip4;
diff --git a/src/vnet/tls/tls.h b/src/vnet/tls/tls.h
index 6bd1371b984..30bcce005fb 100644
--- a/src/vnet/tls/tls.h
+++ b/src/vnet/tls/tls.h
@@ -28,6 +28,10 @@
#define TLS_CHUNK_SIZE (1 << 14)
#define TLS_CA_CERT_PATH "/etc/ssl/certs/ca-certificates.crt"
+#define TLS_INVALID_HANDLE ~0
+#define TLS_IDX_MASK 0x00FFFFFF
+#define TLS_ENGINE_TYPE_SHIFT 28
+
#if TLS_DEBUG
#define TLS_DBG(_lvl, _fmt, _args...) \
if (_lvl <= TLS_DEBUG) \
@@ -98,6 +102,7 @@ typedef struct tls_ctx_
#define parent_app_api_context c_tls_ctx_id.parent_app_api_ctx
#define migration_ctx c_tls_ctx_id.migrate_ctx
+ u32 ts_app_index;
tls_conn_flags_t flags;
u8 *srv_hostname;
u32 evt_index;
@@ -148,6 +153,8 @@ typedef struct tls_engine_vft_
int (*ctx_reinit_cachain) (void);
} tls_engine_vft_t;
+extern tls_engine_vft_t *tls_vfts;
+
tls_main_t *vnet_tls_get_main (void);
void tls_register_engine (const tls_engine_vft_t * vft,
crypto_engine_type_t type);
@@ -160,7 +167,6 @@ int tls_notify_app_connected (tls_ctx_t * ctx, session_error_t err);
void tls_notify_app_enqueue (tls_ctx_t * ctx, session_t * app_session);
void tls_notify_app_io_error (tls_ctx_t *ctx);
void tls_disconnect_transport (tls_ctx_t * ctx);
-int tls_reinit_ca_chain (crypto_engine_type_t tls_engine_id);
void tls_add_postponed_ho_cleanups (u32 ho_index);
void tls_flush_postponed_ho_cleanups ();
diff --git a/src/vnet/tls/tls_inlines.h b/src/vnet/tls/tls_inlines.h
new file mode 100644
index 00000000000..18002730a30
--- /dev/null
+++ b/src/vnet/tls/tls_inlines.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_VNET_TLS_TLS_INLINES_H_
+#define SRC_VNET_TLS_TLS_INLINES_H_
+
+#include <vnet/tls/tls.h>
+
+static inline void
+tls_ctx_parse_handle (u32 ctx_handle, u32 *ctx_index, u32 *engine_type)
+{
+ *ctx_index = ctx_handle & TLS_IDX_MASK;
+ *engine_type = ctx_handle >> TLS_ENGINE_TYPE_SHIFT;
+}
+
+static inline u32
+tls_ctx_alloc (crypto_engine_type_t engine_type)
+{
+ u32 ctx_index;
+ ctx_index = tls_vfts[engine_type].ctx_alloc ();
+ return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
+}
+
+static inline u32
+tls_ctx_alloc_w_thread (crypto_engine_type_t engine_type, u32 thread_index)
+{
+ u32 ctx_index;
+ ctx_index = tls_vfts[engine_type].ctx_alloc_w_thread (thread_index);
+ return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
+}
+
+static inline tls_ctx_t *
+tls_ctx_get (u32 ctx_handle)
+{
+ u32 ctx_index, engine_type;
+ tls_ctx_parse_handle (ctx_handle, &ctx_index, &engine_type);
+ return tls_vfts[engine_type].ctx_get (ctx_index);
+}
+
+static inline tls_ctx_t *
+tls_ctx_get_w_thread (u32 ctx_handle, u8 thread_index)
+{
+ u32 ctx_index, engine_type;
+ tls_ctx_parse_handle (ctx_handle, &ctx_index, &engine_type);
+ return tls_vfts[engine_type].ctx_get_w_thread (ctx_index, thread_index);
+}
+
+static inline void
+tls_ctx_free (tls_ctx_t *ctx)
+{
+ tls_vfts[ctx->tls_ctx_engine].ctx_free (ctx);
+}
+
+static inline int
+tls_ctx_init_server (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_init_server (ctx);
+}
+
+static inline int
+tls_ctx_init_client (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_init_client (ctx);
+}
+
+static inline u32
+tls_ctx_attach (crypto_engine_type_t engine_type, u32 thread_index, void *ctx)
+{
+ u32 ctx_index;
+ ctx_index = tls_vfts[engine_type].ctx_attach (thread_index, ctx);
+ return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
+}
+
+static inline void *
+tls_ctx_detach (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_detach (ctx);
+}
+
+static inline int
+tls_ctx_write (tls_ctx_t *ctx, session_t *app_session,
+ transport_send_params_t *sp)
+{
+ u32 n_wrote;
+
+ sp->max_burst_size = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
+ n_wrote = tls_vfts[ctx->tls_ctx_engine].ctx_write (ctx, app_session, sp);
+ sp->bytes_dequeued = n_wrote;
+ return n_wrote > 0 ? clib_max (n_wrote / TRANSPORT_PACER_MIN_MSS, 1) : 0;
+}
+
+static inline int
+tls_ctx_read (tls_ctx_t *ctx, session_t *tls_session)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_read (ctx, tls_session);
+}
+
+static inline int
+tls_ctx_transport_close (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_transport_close (ctx);
+}
+
+static inline int
+tls_ctx_transport_reset (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_transport_reset (ctx);
+}
+
+static inline int
+tls_ctx_app_close (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_app_close (ctx);
+}
+
+static inline u8
+tls_ctx_handshake_is_over (tls_ctx_t *ctx)
+{
+ return tls_vfts[ctx->tls_ctx_engine].ctx_handshake_is_over (ctx);
+}
+
+static inline int
+tls_reinit_ca_chain (crypto_engine_type_t tls_engine_id)
+{
+ return tls_vfts[tls_engine_id].ctx_reinit_cachain ();
+}
+
+#endif /* SRC_VNET_TLS_TLS_INLINES_H_ */ \ No newline at end of file
diff --git a/src/vnet/tls/tls_record.c b/src/vnet/tls/tls_record.c
new file mode 100644
index 00000000000..af7d54c466b
--- /dev/null
+++ b/src/vnet/tls/tls_record.c
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vnet/tls/tls_record.h>
+
+/*
+ * rfc8446#section-4.1.2
+ * struct {
+ * ProtocolVersion legacy_version = 0x0303; // TLS v1.2
+ * Random random;
+ * opaque legacy_session_id<0..32>;
+ * CipherSuite cipher_suites<2..2^16-2>;
+ * opaque legacy_compression_methods<1..2^8-1>;
+ * Extension extensions<8..2^16-1>;
+ * } ClientHello;
+ */
+tls_handshake_parse_error_t
+tls_handshake_client_hello_parse (u8 *b, int len,
+ tls_handshake_msg_info_t *info)
+{
+ u8 *p = b;
+
+ if (PREDICT_FALSE (len < 2 + 32 + 1 + 2 + 2 + 2))
+ return TLS_HS_PARSE_ERR_INVALID_LEN;
+ /* skip legacy version and random */
+ p += 2 + 32;
+ /* legacy_session_id */
+ info->legacy_session_id_len = *p;
+ info->legacy_session_id = p + 1;
+ p = info->legacy_session_id + info->legacy_session_id_len;
+ if (PREDICT_FALSE (p - b >= len))
+ return TLS_HS_PARSE_ERR_SESSION_ID_LEN;
+ /* cipher_suites */
+ info->cipher_suite_len = clib_net_to_host_u16 (*(u16 *) p);
+ info->cipher_suites = p + 2;
+ p = info->cipher_suites + info->cipher_suite_len;
+ if (PREDICT_FALSE (p - b >= len))
+ return TLS_HS_PARSE_ERR_CIPHER_SUITE_LEN;
+ /* legacy_compression_method, only support null */
+ if (PREDICT_FALSE (*p != 1 || *(p + 1) != 0))
+ return TLS_HS_PARSE_ERR_COMPRESSION_METHOD;
+ p += 2;
+ /* extensions */
+ info->extensions_len = clib_net_to_host_u16 (*(u16 *) p);
+ info->extensions = p + 2;
+ if (PREDICT_FALSE (info->extensions + info->extensions_len - b > len))
+ return TLS_HS_PARSE_ERR_CIPHER_SUITE_LEN;
+
+ return TLS_HS_PARSE_ERR_OK;
+}
+
+typedef tls_handshake_parse_error_t (*tls_handshake_msg_parser) (
+ u8 *b, int len, tls_handshake_msg_info_t *info);
+
+static tls_handshake_msg_parser tls_handshake_msg_parsers[] = {
+ [TLS_HS_CLIENT_HELLO] = tls_handshake_client_hello_parse,
+};
+
+static inline u32
+tls_handshake_ext_requested (const tls_handshake_ext_info_t *req_exts,
+ u32 n_reqs, tls_handshake_ext_type_t ext_type)
+{
+ for (int i = 0; i < n_reqs; i++)
+ {
+ if (req_exts[i].type == ext_type)
+ return i;
+ }
+
+ return ~0;
+}
+
+tls_handshake_parse_error_t
+tls_hanshake_extensions_parse (tls_handshake_msg_info_t *info,
+ tls_handshake_ext_info_t **exts)
+{
+ tls_handshake_ext_info_t *ext;
+ u16 ext_type, ext_len;
+ u8 *b, *b_end;
+
+ ASSERT (info->extensions != 0);
+
+ if (info->extensions_len < 2)
+ return TLS_HS_PARSE_ERR_EXTENSIONS_LEN;
+
+ b = info->extensions;
+ b_end = info->extensions + info->extensions_len;
+
+ while (b < b_end)
+ {
+ ext_type = clib_net_to_host_u16 (*(u16 *) b);
+ b += 2;
+ ext_len = clib_net_to_host_u16 (*(u16 *) b);
+ b += 2;
+
+ if (b + ext_len > b_end)
+ return TLS_HS_PARSE_ERR_EXTENSIONS_LEN;
+
+ vec_add2 (*exts, ext, 1);
+ ext->type = ext_type;
+ ext->len = ext_len;
+ ext->data = b;
+
+ b += ext_len;
+ }
+
+ return TLS_HS_PARSE_ERR_OK;
+}
+
+tls_handshake_parse_error_t
+tls_hanshake_extensions_try_parse (tls_handshake_msg_info_t *info,
+ tls_handshake_ext_info_t *req_exts,
+ u32 n_reqs)
+{
+ u8 *b, *b_end;
+ u16 ext_type, ext_len;
+ u32 n_found = 0, ext_pos;
+
+ ASSERT (info->extensions != 0);
+
+ if (info->extensions_len < 2)
+ return TLS_HS_PARSE_ERR_EXTENSIONS_LEN;
+
+ b = info->extensions;
+ b_end = info->extensions + info->extensions_len;
+
+ while (b < b_end && n_found < n_reqs)
+ {
+ ext_type = clib_net_to_host_u16 (*(u16 *) b);
+ b += 2;
+ ext_len = clib_net_to_host_u16 (*(u16 *) b);
+ b += 2;
+
+ if (b + ext_len > b_end)
+ return TLS_HS_PARSE_ERR_EXTENSIONS_LEN;
+
+ ext_pos = tls_handshake_ext_requested (req_exts, n_reqs, ext_type);
+ if (ext_pos == ~0)
+ {
+ b += ext_len;
+ continue;
+ }
+
+ req_exts[ext_pos].len = ext_len;
+ req_exts[ext_pos].data = b;
+
+ b += ext_len;
+ n_found++;
+ }
+
+ return TLS_HS_PARSE_ERR_OK;
+}
+
+tls_handshake_parse_error_t
+tls_handshake_message_try_parse (u8 *msg, int len,
+ tls_handshake_msg_info_t *info)
+{
+ tls_handshake_msg_t *msg_hdr = (tls_handshake_msg_t *) msg;
+ u8 *b = msg_hdr->message;
+
+ info->len = tls_handshake_message_len (msg_hdr);
+ if (info->len > len)
+ return info->len > TLS_FRAGMENT_MAX_ENC_LEN ?
+ TLS_HS_PARSE_ERR_INVALID_LEN :
+ TLS_HS_PARSE_ERR_WANT_MORE;
+
+ if (msg_hdr->msg_type >= ARRAY_LEN (tls_handshake_msg_parsers) ||
+ !tls_handshake_msg_parsers[msg_hdr->msg_type])
+ return TLS_HS_PARSE_ERR_UNSUPPORTED;
+
+ return tls_handshake_msg_parsers[msg_hdr->msg_type](b, info->len, info);
+}
+
+/**
+ * As per rfc6066#section-3
+ * struct {
+ * NameType name_type;
+ * select (name_type) {
+ * case host_name: HostName;
+ * } name;
+ * } ServerName;
+ *
+ * enum {
+ * host_name(0), (255)
+ * } NameType;
+ *
+ * opaque HostName<1..2^16-1>;
+ *
+ * struct {
+ * ServerName server_name_list<1..2^16-1>
+ * } ServerNameList;
+ */
+tls_handshake_parse_error_t
+tls_handshake_ext_sni_parse (tls_handshake_ext_info_t *ext_info,
+ tls_handshake_ext_t *ext)
+{
+ tls_handshake_ext_sni_t *sni = (tls_handshake_ext_sni_t *) ext;
+ tls_handshake_ext_sni_sn_t *sn;
+ u16 n_names, sn_len;
+ u8 *b, *b_end;
+
+ b = ext_info->data;
+ b_end = b + ext_info->len;
+
+ sni->ext.type = ext_info->type;
+ sni->names = 0;
+ n_names = clib_net_to_host_u16 (*(u16 *) b);
+ b += 2;
+
+ while (b < b_end && vec_len (sni->names) < n_names)
+ {
+ /* only host name supported */
+ if (b[0] != 0)
+ return TLS_HS_PARSE_ERR_EXT_SNI_NAME_TYPE;
+
+ b++;
+ /* server name length */
+ sn_len = clib_net_to_host_u16 (*(u16 *) b);
+ if (sn_len > TLS_EXT_SNI_MAX_LEN)
+ return TLS_HS_PARSE_ERR_EXT_SNI_LEN;
+
+ b += 2;
+
+ vec_add2 (sni->names, sn, 1);
+ sn->name_type = 0;
+ vec_validate (sn->host_name, sn_len - 1);
+ clib_memcpy (sn->host_name, b, sn_len);
+
+ b += sn_len;
+ }
+
+ return TLS_HS_PARSE_ERR_OK;
+}
+
+typedef tls_handshake_parse_error_t (*tls_handshake_ext_parser) (
+ tls_handshake_ext_info_t *ext_info, tls_handshake_ext_t *ext);
+
+static tls_handshake_ext_parser tls_handshake_ext_parsers[] = {
+ [TLS_EXT_SERVER_NAME] = tls_handshake_ext_sni_parse,
+};
+
+tls_handshake_parse_error_t
+tls_handshake_ext_parse (tls_handshake_ext_info_t *ext_info,
+ tls_handshake_ext_t *ext)
+{
+ if (ext_info->type >= ARRAY_LEN (tls_handshake_ext_parsers) ||
+ !tls_handshake_ext_parsers[ext_info->type])
+ return TLS_HS_PARSE_ERR_UNSUPPORTED;
+
+ return tls_handshake_ext_parsers[ext_info->type](ext_info, ext);
+}
+
+static void
+tls_handshake_ext_sni_free (tls_handshake_ext_t *ext)
+{
+ tls_handshake_ext_sni_t *sni = (tls_handshake_ext_sni_t *) ext;
+ tls_handshake_ext_sni_sn_t *sn;
+
+ vec_foreach (sn, sni->names)
+ vec_free (sn->host_name);
+
+ vec_free (sni->names);
+}
+
+typedef void (*tls_handshake_ext_free_fn) (tls_handshake_ext_t *ext);
+
+static tls_handshake_ext_free_fn tls_handshake_ext_free_fns[] = {
+ [TLS_EXT_SERVER_NAME] = tls_handshake_ext_sni_free,
+};
+
+void
+tls_handshake_ext_free (tls_handshake_ext_t *ext)
+{
+ if (ext->type >= ARRAY_LEN (tls_handshake_ext_free_fns) ||
+ !tls_handshake_ext_free_fns[ext->type])
+ return;
+
+ tls_handshake_ext_free_fns[ext->type](ext);
+}
diff --git a/src/vnet/tls/tls_record.h b/src/vnet/tls/tls_record.h
new file mode 100644
index 00000000000..3f7723f06d5
--- /dev/null
+++ b/src/vnet/tls/tls_record.h
@@ -0,0 +1,250 @@
+
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_VNET_TLS_TLS_RECORD_H__
+#define SRC_VNET_TLS_TLS_RECORD_H__
+
+#include <vppinfra/clib.h>
+#include <vppinfra/error.h>
+
+/**
+ * TLS record types as per rfc8446#appendix-B.1
+ */
+#define foreach_tls_content_type \
+ _ (INVALID, 0) \
+ _ (CHANGE_CIPHER_SPEC, 20) \
+ _ (ALERT, 21) \
+ _ (HANDSHAKE, 22) \
+ _ (APPLICATION_DATA, 23) \
+ _ (HEARTBEAT, 24) /* RFC 6520 */
+
+typedef enum tls_record_type_
+{
+#define _(sym, val) TLS_REC_##sym = val,
+ foreach_tls_content_type
+#undef _
+} __clib_packed tls_record_type_t;
+
+typedef struct tls_protocol_version_
+{
+ u8 major;
+ u8 minor;
+} __clib_packed tls_protocol_version_t;
+
+#define TLS_MAJOR_VERSION 3
+#define TLS_MINOR_VERSION_MIN 0 /**< SSLv3 */
+#define TLS_MINOR_VERSION_MAX 4 /**< TLS1.3 */
+
+typedef struct tls_record_header_
+{
+ tls_record_type_t type; /**< content type */
+ tls_protocol_version_t version; /**< version (deprecated) */
+ u16 length; /**< fragment length */
+ u8 fragment[0]; /**< fragment/payload */
+} __clib_packed tls_record_header_t;
+
+#define TLS_FRAGMENT_MAX_LEN (1 << 14) /**< 16KB rfc8446 */
+/** rfc5246 (TLS1.2) allows 2048 bytes of protection */
+#define TLS12_FRAGMENT_MAX_ENC_LEN (TLS_FRAGMENT_MAX_LEN + (2 << 10))
+#define TLS13_FRAGMENT_MAX_ENC_LEN (TLS_FRAGMENT_MAX_LEN + 256)
+#define TLS_FRAGMENT_MAX_ENC_LEN TLS12_FRAGMENT_MAX_ENC_LEN
+
+/*
+ * Handshake message types as per rfc8446#appendix-B.3
+ */
+#define foreach_tls_handshake_type \
+ _ (HELLO_REQUEST, 0) \
+ _ (CLIENT_HELLO, 1) \
+ _ (SERVER_HELLO, 2) \
+ _ (HELLO_VERIFY_REQUEST, 3) \
+ _ (NEW_SESSION_TICKET, 4) \
+ _ (END_OF_EARLY_DATA, 5) \
+ _ (HELLO_RETRY_REQUEST, 6) \
+ _ (ENCRYPTED_EXTENSIONS, 8) \
+ _ (CERTIFICATE, 11) \
+ _ (SERVER_KEY_EXCHANGE, 12) \
+ _ (CERTIFICATE_REQUEST, 13) \
+ _ (SERVER_HELLO_DONE, 14) \
+ _ (CERTIFICATE_VERIFY, 15) \
+ _ (CLIENT_KEY_EXCHANGE, 16) \
+ _ (FINISHED, 20) \
+ _ (CERTIFICATE_URL, 21) \
+ _ (CERTIFICATE_STATUS, 22) \
+ _ (SUPPLEMENTAL_DATA, 23) \
+ _ (KEY_UPDATE, 24) \
+ _ (MESSAGE_HASH, 254)
+
+typedef enum tls_handshake_type_
+{
+#define _(sym, val) TLS_HS_##sym = val,
+ foreach_tls_handshake_type
+#undef _
+} tls_handshake_type_t;
+
+typedef struct
+{
+ u32 msg_type : 8; /**< message type */
+ u32 length : 24; /**< message length */
+ u8 message[0]; /**< message contents */
+} __clib_packed tls_handshake_msg_t;
+
+static inline u32
+tls_handshake_message_len (tls_handshake_msg_t *msg)
+{
+ u8 *p = (u8 *) msg;
+ return p[1] << 16 | p[2] << 8 | p[3];
+}
+
+/**
+ * https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
+ */
+#define foreach_tls_hanshake_extensions \
+ _ (SERVER_NAME, 0) \
+ _ (MAX_FRAGMENT_LENGTH, 1) \
+ _ (STATUS_REQUEST, 5) \
+ _ (SUPPORTED_GROUPS, 10) \
+ _ (EC_POINT_FORMATS, 11) \
+ _ (SIGNATURE_ALGORITHMS, 13) \
+ _ (APPLICATION_LAYER_PROTOCOL_NEGOTIATION, 16) \
+ _ (SIGNED_CERTIFICATE_TIMESTAMP, 18) \
+ _ (CLIENT_CERTIFICATE_TYPE, 19) \
+ _ (SERVER_CERTIFICATE_TYPE, 20) \
+ _ (PADDING, 21) \
+ _ (TOKEN_BINDING, 24) \
+ _ (RECORD_SIZE_LIMIT, 28) \
+ _ (SESSION_TICKET, 35) \
+ _ (PRE_SHARED_KEY, 41) \
+ _ (EARLY_DATA, 42) \
+ _ (SUPPORTED_VERSIONS, 43) \
+ _ (COOKIE, 44) \
+ _ (PSK_KEY_EXCHANGE_MODES, 45) \
+ _ (CERTIFICATE_AUTHORITIES, 47) \
+ _ (OID_FILTERS, 48) \
+ _ (SIGNATURE_ALGORITHMS_CERT, 50) \
+ _ (POST_HANDSHAKE_AUTH, 49) \
+ _ (KEY_SHARE, 51) \
+ _ (CONNECTION_ID, 54) \
+ _ (QUIC_TRANSPORT_PARAMETERS, 57) \
+ _ (TICKET_REQUEST, 58) \
+ _ (DNSSEC_CHAIN, 59)
+
+typedef enum tls_handshake_extension_type_
+{
+#define _(sym, val) TLS_EXT_##sym = val,
+ foreach_tls_hanshake_extensions
+#undef _
+} tls_handshake_ext_type_t;
+
+/* Base struct for all extensions */
+typedef struct tls_handshake_ext_
+{
+ tls_handshake_ext_type_t type;
+ u8 extension[0];
+} tls_handshake_ext_t;
+
+typedef struct tls_handshake_ext_server_name_
+{
+ u8 name_type;
+ u8 *host_name;
+} tls_handshake_ext_sni_sn_t;
+
+typedef struct tls_handshake_ext_sni_
+{
+ tls_handshake_ext_t ext;
+ tls_handshake_ext_sni_sn_t *names;
+} tls_handshake_ext_sni_t;
+
+/* FQDN length as per rfc1035 */
+#define TLS_EXT_SNI_MAX_LEN 255
+
+#define foreach_tls_handshake_parse_error \
+ _ (OK, "ok") \
+ _ (WANT_MORE, "want_more") \
+ _ (UNSUPPORTED, "unsupported") \
+ _ (INVALID_LEN, "invalid_len") \
+ _ (SESSION_ID_LEN, "session_id_len") \
+ _ (CIPHER_SUITE_LEN, "cipher_suite_len") \
+ _ (COMPRESSION_METHOD, "compression_method") \
+ _ (EXTENSIONS_LEN, "extensions_len") \
+ _ (EXT_SNI_NAME_TYPE, "ext_sni_name_type") \
+ _ (EXT_SNI_LEN, "ext_sni_len")
+
+typedef enum tls_handshake_parse_error_
+{
+#define _(sym, str) TLS_HS_PARSE_ERR_##sym,
+ foreach_tls_handshake_parse_error
+#undef _
+} tls_handshake_parse_error_t;
+
+typedef struct tls_hanshake_ext_info_
+{
+ tls_handshake_ext_type_t type;
+ u16 len;
+ u8 *data;
+} tls_handshake_ext_info_t;
+
+typedef struct tls_handshake_msg_info_
+{
+ tls_handshake_type_t type;
+ u32 len;
+ u8 legacy_session_id_len;
+ u8 *legacy_session_id;
+ u16 cipher_suite_len;
+ u8 *cipher_suites;
+ u16 extensions_len;
+ u8 *extensions;
+} tls_handshake_msg_info_t;
+
+static inline u8
+tls_record_type_is_valid (tls_record_type_t type)
+{
+ switch (type)
+ {
+ case TLS_REC_CHANGE_CIPHER_SPEC:
+ case TLS_REC_ALERT:
+ case TLS_REC_HANDSHAKE:
+ case TLS_REC_APPLICATION_DATA:
+ case TLS_REC_HEARTBEAT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline u8
+tls_record_hdr_is_valid (tls_record_header_t rec_hdr)
+{
+ u16 rec_len;
+
+ if (!tls_record_type_is_valid (rec_hdr.type))
+ return 0;
+
+ /* Support for SSLv3 and TLS1.0 to TLS1.3 */
+ if (rec_hdr.version.major != TLS_MAJOR_VERSION)
+ return 0;
+
+ rec_len = clib_net_to_host_u16 (rec_hdr.length);
+ if (rec_len == 0 || rec_len > TLS_FRAGMENT_MAX_ENC_LEN)
+ return 0;
+
+ return 1;
+}
+
+tls_handshake_parse_error_t
+tls_handshake_message_try_parse (u8 *msg, int len,
+ tls_handshake_msg_info_t *info);
+tls_handshake_parse_error_t
+tls_hanshake_extensions_parse (tls_handshake_msg_info_t *info,
+ tls_handshake_ext_info_t **exts);
+tls_handshake_parse_error_t
+tls_hanshake_extensions_try_parse (tls_handshake_msg_info_t *info,
+ tls_handshake_ext_info_t *req_exts,
+ u32 n_reqs);
+tls_handshake_parse_error_t
+tls_handshake_ext_parse (tls_handshake_ext_info_t *ext_info,
+ tls_handshake_ext_t *ext);
+void tls_handshake_ext_free (tls_handshake_ext_t *ext);
+
+#endif /* SRC_VNET_TLS_TLS_RECORD_H__ */
diff --git a/src/vnet/udp/udp.c b/src/vnet/udp/udp.c
index b3c02510232..8deeb9e41ee 100644
--- a/src/vnet/udp/udp.c
+++ b/src/vnet/udp/udp.c
@@ -232,18 +232,43 @@ udp_session_get_listener (u32 listener_index)
return &us->connection;
}
+always_inline u16
+udp_compute_checksum (vlib_main_t *vm, vlib_buffer_t *b, u8 csum_offload,
+ u8 is_ip4)
+{
+ u16 csum = 0;
+
+ if (csum_offload)
+ vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
+ else
+ {
+ if (is_ip4)
+ csum =
+ ip4_tcp_udp_compute_checksum (vm, b, vlib_buffer_get_current (b));
+ else
+ {
+ int bogus = 0;
+ csum = ip6_tcp_udp_icmp_compute_checksum (
+ vm, b, vlib_buffer_get_current (b), &bogus);
+ }
+ }
+
+ return csum;
+}
+
always_inline u32
udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b,
u8 is_cless)
{
+ udp_header_t *uh;
+
b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
/* reuse tcp medatada for now */
vnet_buffer (b)->tcp.connection_index = uc->c_c_index;
if (!is_cless)
{
- vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port,
- udp_csum_offload (uc));
+ uh = vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port);
if (uc->c_is_ip4)
vlib_buffer_push_ip4_custom (vm, b, &uc->c_lcl_ip4, &uc->c_rmt_ip4,
@@ -263,8 +288,7 @@ udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b,
hdr = *(session_dgram_hdr_t *) (data - sizeof (hdr));
/* Local port assumed to be bound, not overwriting it */
- vlib_buffer_push_udp (b, uc->c_lcl_port, hdr.rmt_port,
- udp_csum_offload (uc));
+ uh = vlib_buffer_push_udp (b, uc->c_lcl_port, hdr.rmt_port);
if (uc->c_is_ip4)
vlib_buffer_push_ip4_custom (vm, b, &hdr.lcl_ip.ip4, &hdr.rmt_ip.ip4,
@@ -279,6 +303,9 @@ udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b,
vnet_buffer (b)->tcp.flags |= UDP_CONN_F_LISTEN;
}
+ uh->checksum =
+ udp_compute_checksum (vm, b, udp_csum_offload (uc), uc->c_is_ip4);
+
return 0;
}
@@ -563,6 +590,9 @@ udp_enable_disable (vlib_main_t *vm, u8 is_en)
{
udp_main_t *um = &udp_main;
+ if (!is_en || um->is_init)
+ return 0;
+
/* Not ideal. The sparse vector used to map ports to next nodes assumes
* only a few ports are ever used. When udp transport is enabled this does
* not hold and, to make matters worse, ports are consumed in a random
@@ -583,6 +613,7 @@ udp_enable_disable (vlib_main_t *vm, u8 is_en)
vec_validate (um->transport_ports_refcnt[0], 65535);
vec_validate (um->transport_ports_refcnt[1], 65535);
+ um->is_init = 1;
return 0;
}
diff --git a/src/vnet/udp/udp.h b/src/vnet/udp/udp.h
index 8e4e87f85a8..c6f867500e0 100644
--- a/src/vnet/udp/udp.h
+++ b/src/vnet/udp/udp.h
@@ -154,6 +154,7 @@ typedef struct
u16 default_mtu;
u16 msg_id_base;
u8 csum_offload;
+ u8 is_init;
u8 icmp_send_unreachable_disabled;
} udp_main_t;
diff --git a/src/vnet/udp/udp_error.def b/src/vnet/udp/udp_error.def
index ef19970ce72..843aacfc6ef 100644
--- a/src/vnet/udp/udp_error.def
+++ b/src/vnet/udp/udp_error.def
@@ -28,3 +28,4 @@ udp_error (CREATE_SESSION, create_session, ERROR, "Failed to create session")
udp_error (MQ_FULL, mq_full, ERROR, "Application msg queue full")
udp_error (INVALID_CONNECTION, invalid_connection, ERROR, "Invalid connection")
udp_error (PKTS_SENT, pkts_sent, INFO, "Packets sent")
+udp_error (CONNECTED, connected, INFO, "Connected session")
diff --git a/src/vnet/udp/udp_inlines.h b/src/vnet/udp/udp_inlines.h
index f0dd44f48b5..ceec0b191b1 100644
--- a/src/vnet/udp/udp_inlines.h
+++ b/src/vnet/udp/udp_inlines.h
@@ -26,7 +26,7 @@
#include <vnet/udp/udp_encap.h>
always_inline void *
-vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum)
+vlib_buffer_push_udp (vlib_buffer_t *b, u16 sp, u16 dp)
{
udp_header_t *uh;
u16 udp_len = sizeof (udp_header_t) + b->current_length;
@@ -38,8 +38,6 @@ vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum)
uh->dst_port = dp;
uh->checksum = 0;
uh->length = clib_host_to_net_u16 (udp_len);
- if (offload_csum)
- vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
vnet_buffer (b)->l4_hdr_offset = (u8 *) uh - b->data;
b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
return uh;
diff --git a/src/vnet/udp/udp_input.c b/src/vnet/udp/udp_input.c
index a90461186c1..693824f9628 100644
--- a/src/vnet/udp/udp_input.c
+++ b/src/vnet/udp/udp_input.c
@@ -272,6 +272,7 @@ udp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
uc0->sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
if (uc0->flags & UDP_CONN_F_CONNECTED)
{
+ error0 = UDP_ERROR_CONNECTED;
if (s0->thread_index != thread_index)
{
/*
diff --git a/src/vnet/unix/gdb_funcs.c b/src/vnet/unix/gdb_funcs.c
index d6fdc985bd9..a89b7202400 100644
--- a/src/vnet/unix/gdb_funcs.c
+++ b/src/vnet/unix/gdb_funcs.c
@@ -238,44 +238,44 @@ gdb_show_traces ()
/* Get active traces from pool. */
- foreach_vlib_main ()
- {
- fmt = "------------------- Start of thread %d %s -------------------\n";
- s = format (s, fmt, index, vlib_worker_threads[index].name);
+ foreach_vlib_main__ (0 /* no checks */)
+ {
+ fmt = "------------------- Start of thread %d %s -------------------\n";
+ s = format (s, fmt, index, vlib_worker_threads[index].name);
- tm = &this_vlib_main->trace_main;
+ tm = &this_vlib_main->trace_main;
- trace_apply_filter (this_vlib_main);
+ trace_apply_filter (this_vlib_main);
- traces = 0;
- pool_foreach (h, tm->trace_buffer_pool)
- {
- vec_add1 (traces, h[0]);
- }
+ traces = 0;
+ pool_foreach (h, tm->trace_buffer_pool)
+ {
+ vec_add1 (traces, h[0]);
+ }
- if (vec_len (traces) == 0)
- {
- s = format (s, "No packets in trace buffer\n");
- goto done;
- }
+ if (vec_len (traces) == 0)
+ {
+ s = format (s, "No packets in trace buffer\n");
+ goto done;
+ }
- /* Sort them by increasing time. */
- vec_sort_with_function (traces, trace_cmp);
+ /* Sort them by increasing time. */
+ vec_sort_with_function (traces, trace_cmp);
- for (i = 0; i < vec_len (traces); i++)
- {
- if (i == max)
- {
- fformat (stderr,
- "Limiting display to %d packets."
- " To display more specify max.",
- max);
- goto done;
- }
-
- s = format (s, "Packet %d\n%U\n\n", i + 1, format_vlib_trace,
- vlib_get_first_main (), traces[i]);
- }
+ for (i = 0; i < vec_len (traces); i++)
+ {
+ if (i == max)
+ {
+ fformat (stderr,
+ "Limiting display to %d packets."
+ " To display more specify max.",
+ max);
+ goto done;
+ }
+
+ s = format (s, "Packet %d\n%U\n\n", i + 1, format_vlib_trace,
+ vlib_get_first_main (), traces[i]);
+ }
done:
vec_free (traces);
diff --git a/src/vpp-api/python/pyproject.toml b/src/vpp-api/python/pyproject.toml
new file mode 100644
index 00000000000..638dd9c54fc
--- /dev/null
+++ b/src/vpp-api/python/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py
index d724cb33ce9..707bb03b790 100644
--- a/src/vpp-api/python/vpp_papi/vpp_serializer.py
+++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py
@@ -644,10 +644,15 @@ class VPPType(Packer):
else:
arg = data[a]
kwarg = kwargs[a] if a in kwargs else None
- if isinstance(self.packers[i], VPPType):
- b += self.packers[i].pack(arg, kwarg)
- else:
- b += self.packers[i].pack(arg, kwargs)
+ try:
+ if isinstance(self.packers[i], VPPType):
+ b += self.packers[i].pack(arg, kwarg)
+ else:
+ b += self.packers[i].pack(arg, kwargs)
+ except Exception as e:
+ raise VPPSerializerValueError(
+ f"Exception while packing {data} for {self.name}.{a}."
+ ) from e
return bytes(b)
diff --git a/src/vpp-api/vapi/vapi.c b/src/vpp-api/vapi/vapi.c
index 022f023aeb0..26c5708342f 100644
--- a/src/vpp-api/vapi/vapi.c
+++ b/src/vpp-api/vapi/vapi.c
@@ -1792,14 +1792,15 @@ vapi_verify_msg_size (vapi_msg_id_t id, void *buf, uword buf_size)
}
vapi_error_e
-vapi_dispatch_one (vapi_ctx_t ctx)
+vapi_dispatch_one_timedwait (vapi_ctx_t ctx, u32 wait_time)
{
VAPI_DBG ("vapi_dispatch_one()");
void *msg;
uword size;
svm_q_conditional_wait_t cond =
- vapi_is_nonblocking (ctx) ? SVM_Q_NOWAIT : SVM_Q_WAIT;
- vapi_error_e rv = vapi_recv (ctx, &msg, &size, cond, 0);
+ vapi_is_nonblocking (ctx) ? (wait_time ? SVM_Q_TIMEDWAIT : SVM_Q_NOWAIT) :
+ SVM_Q_WAIT;
+ vapi_error_e rv = vapi_recv (ctx, &msg, &size, cond, wait_time);
if (VAPI_OK != rv)
{
VAPI_DBG ("vapi_recv failed with rv=%d", rv);
@@ -1847,6 +1848,12 @@ done:
}
vapi_error_e
+vapi_dispatch_one (vapi_ctx_t ctx)
+{
+ return vapi_dispatch_one_timedwait (ctx, 0);
+}
+
+vapi_error_e
vapi_dispatch (vapi_ctx_t ctx)
{
vapi_error_e rv = VAPI_OK;
diff --git a/src/vpp-api/vapi/vapi.h b/src/vpp-api/vapi/vapi.h
index 970c5080667..8f092eed1c2 100644
--- a/src/vpp-api/vapi/vapi.h
+++ b/src/vpp-api/vapi/vapi.h
@@ -225,6 +225,18 @@ vapi_error_e vapi_wait (vapi_ctx_t ctx);
/**
* @brief pick next message sent by vpp and call the appropriate callback
*
+ * @note if using block mode, it will be blocked indefinitely until the next
+ * msg available. If using non-blocking mode, it will block for time_wait
+ * seconds until the next msg available if time_wait > 0, or does not block if
+ * time_wait == 0.
+ *
+ * @return VAPI_OK on success, other error code on error
+ */
+vapi_error_e vapi_dispatch_one_timedwait (vapi_ctx_t ctx, u32 wait_time);
+
+/**
+ * @brief pick next message sent by vpp and call the appropriate callback
+ *
* @return VAPI_OK on success, other error code on error
*/
vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
@@ -235,11 +247,11 @@ vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
*
* @note the dispatch loop is interrupted if any error is encountered or
* returned from the callback, in which case this error is returned as the
- * result of vapi_dispatch. In this case it might be necessary to call dispatch
- * again to process the remaining messages. Returning VAPI_EUSER from
- * a callback allows the user to break the dispatch loop (and distinguish
- * this case in the calling code from other failures). VAPI never returns
- * VAPI_EUSER on its own.
+ * result of vapi_dispatch. In this case it might be necessary to call
+ * dispatch again to process the remaining messages. Returning VAPI_EUSER
+ * from a callback allows the user to break the dispatch loop (and
+ * distinguish this case in the calling code from other failures). VAPI never
+ * returns VAPI_EUSER on its own.
*
* @return VAPI_OK on success, other error code on error
*/
diff --git a/src/vpp/api/types.c b/src/vpp/api/types.c
index 92bbdb30ac9..8e118109492 100644
--- a/src/vpp/api/types.c
+++ b/src/vpp/api/types.c
@@ -49,21 +49,6 @@ format_vl_api_address (u8 * s, va_list * args)
}
u8 *
-format_vl_api_address_union (u8 * s, va_list * args)
-{
- const vl_api_address_union_t *addr =
- va_arg (*args, vl_api_address_union_t *);
- vl_api_address_family_t af = va_arg (*args, int);
-
- if (ADDRESS_IP6 == af)
- s = format (s, "%U", format_ip6_address, addr->ip6);
- else
- s = format (s, "%U", format_ip4_address, addr->ip4);
-
- return s;
-}
-
-u8 *
format_vl_api_ip4_address (u8 * s, va_list * args)
{
const vl_api_ip4_address_t *addr = va_arg (*args, vl_api_ip4_address_t *);
diff --git a/src/vpp/api/types.h b/src/vpp/api/types.h
index 9864e8c38a8..7a546dbdc0d 100644
--- a/src/vpp/api/types.h
+++ b/src/vpp/api/types.h
@@ -40,7 +40,6 @@ extern u8 *format_vl_api_address (u8 * s, va_list * args);
extern u8 *format_vl_api_address_family (u8 * s, va_list * args);
extern u8 *format_vl_api_ip4_address (u8 * s, va_list * args);
extern u8 *format_vl_api_ip6_address (u8 * s, va_list * args);
-extern u8 *format_vl_api_address_union (u8 * s, va_list * args);
extern u8 *format_vl_api_prefix (u8 * s, va_list * args);
extern u8 *format_vl_api_mprefix (u8 * s, va_list * args);
extern u8 *format_vl_api_mac_address (u8 * s, va_list * args);
diff --git a/src/vpp/conf/startup.conf b/src/vpp/conf/startup.conf
index 929106a4c8a..a30a15ab2b1 100644
--- a/src/vpp/conf/startup.conf
+++ b/src/vpp/conf/startup.conf
@@ -164,6 +164,11 @@ cpu {
# num-rx-queues 2
# }
+ ## Set interface only in poll mode
+ # dev 0000:02:00.1 {
+ # no-rx-interrupts
+ # }
+
## Change UIO driver used by VPP, Options are: igb_uio, vfio-pci,
## uio_pci_generic or auto (default)
# uio-driver vfio-pci
diff --git a/src/vpp/vnet/main.c b/src/vpp/vnet/main.c
index c57efd59a62..dd4f4cc3353 100644
--- a/src/vpp/vnet/main.c
+++ b/src/vpp/vnet/main.c
@@ -329,6 +329,10 @@ defaulted:
unformat_free (&input);
+ /* if main thread affinity is unspecified, set to current running cpu */
+ if (main_core == ~0)
+ main_core = sched_getcpu ();
+
/* set process affinity for main thread */
if (main_core != ~0)
{
diff --git a/src/vppinfra/CMakeLists.txt b/src/vppinfra/CMakeLists.txt
index 5878f0612f0..83a8b2a7e57 100644
--- a/src/vppinfra/CMakeLists.txt
+++ b/src/vppinfra/CMakeLists.txt
@@ -14,6 +14,26 @@
enable_language(ASM)
##############################################################################
+# find libdl
+##############################################################################
+list(APPEND VPPINFRA_LIBS ${CMAKE_DL_LIBS})
+
+##############################################################################
+# find libunwind
+##############################################################################
+vpp_find_path(LIBUNWIND_INCLUDE_DIR unwind.h)
+vpp_find_library(LIBUNWIND_LIB NAMES unwind libunwind)
+
+if (LIBUNWIND_INCLUDE_DIR AND LIBUNWIND_LIB)
+ message(STATUS "libunwind found at ${LIBUNWIND_LIB}")
+ list(APPEND VPPINFRA_LIBS ${LIBUNWIND_LIB})
+ add_definitions(-DHAVE_LIBUNWIND=1)
+else()
+ message(WARNING "libunwind not found - stack traces disabled")
+ add_definitions(-DHAVE_LIBUNWIND=0)
+endif()
+
+##############################################################################
# Generate vppinfra/config.h
##############################################################################
set(LOG2_CACHE_LINE_BYTES ${VPP_LOG2_CACHE_LINE_SIZE})
@@ -42,15 +62,14 @@ add_definitions(-fvisibility=hidden)
set_source_files_properties( cJSON.c jsonformat.c PROPERTIES
COMPILE_DEFINITIONS " CJSON_API_VISIBILITY " )
-
##############################################################################
# vppinfra sources
##############################################################################
set(VPPINFRA_SRCS
- backtrace.c
bitmap.c
bihash_all_vector.c
cpu.c
+ devicetree.c
dlmalloc.c
elf.c
elog.c
@@ -80,6 +99,7 @@ set(VPPINFRA_SRCS
rbtree.c
serialize.c
socket.c
+ stack.c
std-formats.c
string.c
time.c
@@ -132,6 +152,7 @@ set(VPPINFRA_HEADERS
crypto/aes_ctr.h
crypto/aes_gcm.h
crypto/poly1305.h
+ devicetree.h
dlist.h
dlmalloc.h
elf_clib.h
@@ -142,6 +163,7 @@ set(VPPINFRA_HEADERS
fifo.h
file.h
format.h
+ format_ansi.h
format_table.h
hash.h
heap.h
@@ -175,6 +197,7 @@ set(VPPINFRA_HEADERS
smp.h
socket.h
sparse_vec.h
+ stack.h
string.h
time.h
time_range.h
@@ -229,18 +252,9 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
)
endif()
-if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
- option(VPP_USE_EXTERNAL_LIBEXECINFO "Use external libexecinfo (useful for non-glibc targets)." ON)
-else()
- option(VPP_USE_EXTERNAL_LIBEXECINFO "Use external libexecinfo (useful for non-glibc targets)." OFF)
-endif()
-
-if(VPP_USE_EXTERNAL_LIBEXECINFO)
- set(EXECINFO_LIB execinfo)
-endif()
add_vpp_library(vppinfra
SOURCES ${VPPINFRA_SRCS}
- LINK_LIBRARIES m ${EXECINFO_LIB}
+ LINK_LIBRARIES m ${VPPINFRA_LIBS}
INSTALL_HEADERS ${VPPINFRA_HEADERS}
COMPONENT libvppinfra
LTO
@@ -265,6 +279,7 @@ if(VPP_BUILD_VPPINFRA_TESTS)
longjmp
macros
maplog
+ mhash
pmalloc
pool_alloc
pool_iterate
diff --git a/src/vppinfra/asm_mips.h b/src/vppinfra/asm_mips.h
deleted file mode 100644
index 7c9e69586f4..00000000000
--- a/src/vppinfra/asm_mips.h
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- Copyright (c) 2004 Eliot Dresselhaus
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef included_asm_mips_h
-#define included_asm_mips_h
-
-/* Encoding of MIPS instructions. */
-/* Encoding of opcode field (op). */
-#define mips_foreach_opcode \
- _(SPECIAL) _(REGIMM) _(j) _(jal) _(beq) _(bne) _(blez) _(bgtz) \
- _(addi) _(addiu) _(slti) _(sltiu) _(andi) _(ori) _(xori) _(lui) \
- _(COP0) _(COP1) _(COP2) _(COP1X) _(beql) _(bnel) _(blezl) _(bgtzl) \
- _(daddi) _(daddiu) _(ldl) _(ldr) _(SPECIAL2) _(jalx) _(MDMX) _(O37) \
- _(lb) _(lh) _(lwl) _(lw) _(lbu) _(lhu) _(lwr) _(lwu) \
- _(sb) _(sh) _(swl) _(sw) _(sdl) _(sdr) _(swr) _(cache) \
- _(ll) _(lwc1) _(lwc2) _(pref) _(lld) _(ldc1) _(ldc2) _(ld) \
- _(sc) _(swc1) _(swc2) _(o73) _(scd) _(sdc1) _(sdc2) _(sd)
-
-/* Encoding of funct field. */
-#define mips_foreach_special_funct \
- _(sll) _(MOVCI) _(srl) _(sra) _(sllv) _(o05) _(srlv) _(srav) \
- _(jr) _(jalr) _(movz) _(movn) _(syscall) _(break) _(o16) _(sync) \
- _(mfhi) _(mthi) _(mflo) _(mtlo) _(dsllv) _(o25) _(dsrlv) _(dsrav) \
- _(mult) _(multu) _(div) _(divu) _(dmult) _(dmultu) _(ddiv) _(ddivu) \
- _(add) _(addu) _(sub) _(subu) _(and) _(or) _(xor) _(nor) \
- _(o50) _(o51) _(slt) _(sltu) _(dadd) _(daddu) _(dsub) _(dsubu) \
- _(tge) _(tgeu) _(tlt) _(tltu) _(teq) _(o65) _(tne) _(o67) \
- _(dsll) _(o71) _(dsrl) _(dsra) _(dsll32) _(o75) _(dsrl32) _(dsra32)
-
-/* SPECIAL2 encoding of funct field. */
-#define mips_foreach_special2_funct \
- _(madd) _(maddu) _(mul) _(o03) _(msub) _(msubu) _(o06) _(o07) \
- _(o10) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \
- _(o20) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \
- _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) \
- _(clz) _(clo) _(o42) _(o43) _(dclz) _(dclo) _(o46) _(o47) \
- _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \
- _(o60) _(o61) _(o62) _(o63) _(o64) _(o65) _(o66) _(o67) \
- _(o70) _(o71) _(o72) _(o73) _(o74) _(o75) _(o76) _(sdbbp)
-
-/* REGIMM encoding of rt field. */
-#define mips_foreach_regimm_rt \
- _(bltz) _(bgez) _(bltzl) _(bgezl) _(o04) _(o05) _(o06) _(o07) \
- _(tgei) _(tgeiu) _(tltiu) _(teqi) _(o14) _(tnei) _(o16) _(o17) \
- _(bltzal) _(bgezal) _(bltzall) _(bgezall) _(o24) _(o25) _(o26) _(o27) \
- _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37)
-
-/* COP0 encoding of rs field. */
-#define mips_foreach_cop0_rs \
- _(mfc0) _(dmfc0) _(o02) _(o03) _(mtc0) _(dmtc0) _(o06) _(o07) \
- _(o10) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \
- _(C0) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \
- _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37)
-
-/* COP0 encoding of funct when rs == RS_CO */
-#define mips_foreach_cop0_funct \
- _(o00) _(tlbr) _(tlbwi) _(o03) _(o04) _(o05) _(tlbwr) _(o07) \
- _(tlbp) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \
- _(o20) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \
- _(eret) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(deret) \
- _(wait) _(o41) _(o42) _(o43) _(o44) _(o45) _(o46) _(o47) \
- _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \
- _(o60) _(o61) _(o62) _(o63) _(o64) _(o65) _(o66) _(o67) \
- _(o70) _(o71) _(o72) _(o73) _(o74) _(o75) _(o76) _(o77)
-
-/* COP1 encoding of rs field. */
-#define mips_foreach_cop1_rs \
- _(mfc1) _(dmfc1) _(cfc1) _(o03) _(mtc1) _(dmtc1) _(ctc1) _(o07) \
- _(BC1) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \
- _(S) _(D) _(o22) _(o23) _(W) _(L) _(o26) _(o27) \
- _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37)
-
-/* COP1 encoding of funct for S and D */
-#define mips_foreach_cop1_funct \
- _(add) _(sub) _(mul) _(div) _(sqrt) _(abs) _(mov) _(neg) \
- _(roundl) _(truncl) _(ceill) _(floorl) _(roundw) _(truncw) _(ceilw) _(floorw) \
- _(o20) _(MOVCF) _(movz) _(movn) _(o24) _(recip) _(rsqrt) _(o27) \
- _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) \
- _(cvts) _(cvtd) _(o42) _(o43) _(cvtw) _(cvtl) _(o46) _(o47) \
- _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \
- _(cf) _(cun) _(ceq) _(cueq) _(colt) _(cult) _(cole) _(cule) \
- _(csf) _(cngle) _(cseq) _(cngl) _(clt) _(cnge) _(cle) _(cngt)
-
-/* COP1X encoding of funct */
-#define mips_foreach_cop1x_funct \
- _(lwxc1) _(ldxc1) _(o02) _(o03) _(o04) _(luxc1) _(o06) _(o07) \
- _(swxc1) _(sdxc1) _(o12) _(o13) _(o14) _(suxc1) _(o16) _(prefx) \
- _(o20) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \
- _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) \
- _(madds) _(maddd) _(o42) _(o43) _(o44) _(o45) _(o46) _(o47) \
- _(msubs) _(msubd) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \
- _(nmadds) _(nmaddd) _(o62) _(o63) _(o64) _(o65) _(o66) _(o67) \
- _(nmsubs) _(nmsubd) _(o72) _(o73) _(o74) _(o75) _(o76) _(o77)
-
-#define mips_foreach_mdmx_funct \
- _(msgn) _(ceq) _(pickf) _(pickt) _(clt) _(cle) _(min) _(max) \
- _(o10) _(o11) _(sub) _(add) _(and) _(xor) _(or) _(nor) \
- _(sll) _(o21) _(srl) _(sra) _(o24) _(o25) _(o26) _(o27) \
- _(alniob) _(alnvob) _(alniqh) _(alnvqh) _(o34) _(o35) _(o36) _(shfl) \
- _(rzu) _(rnau) _(rneu) _(o43) _(rzs) _(rnas) _(rnes) _(o47) \
- _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \
- _(mul) _(o61) _(muls) _(mula) _(o64) _(o65) _(suba) _(adda) \
- _(o70) _(o71) _(o72) _(o73) _(o74) _(o75) _(wac) _(rac)
-
-#define _(f) MIPS_OPCODE_##f,
-typedef enum
-{
- mips_foreach_opcode
-} mips_insn_opcode_t;
-#undef _
-
-#define _(f) MIPS_SPECIAL_FUNCT_##f,
-typedef enum
-{
- mips_foreach_special_funct
-} mips_insn_special_funct_t;
-#undef _
-
-#define _(f) MIPS_SPECIAL2_FUNCT_##f,
-typedef enum
-{
- mips_foreach_special2_funct
-} mips_insn_special2_funct_t;
-#undef _
-
-#define _(f) MIPS_REGIMM_RT_##f,
-typedef enum
-{
- mips_foreach_regimm_rt
-} mips_insn_regimm_rt_t;
-#undef _
-
-#define _(f) MIPS_COP0_RS_##f,
-typedef enum
-{
- mips_foreach_cop0_rs
-} mips_insn_cop0_rs_t;
-#undef _
-
-#define _(f) MIPS_COP0_FUNCT_##f,
-typedef enum
-{
- mips_foreach_cop0_funct
-} mips_insn_cop0_funct_t;
-#undef _
-
-#define _(f) MIPS_COP1_RS_##f,
-typedef enum
-{
- mips_foreach_cop1_rs
-} mips_insn_cop1_rs_t;
-#undef _
-
-#define _(f) MIPS_COP1_FUNCT_##f,
-typedef enum
-{
- mips_foreach_cop1_funct
-} mips_insn_cop1_funct_t;
-#undef _
-
-#define _(f) MIPS_COP1X_FUNCT_##f,
-typedef enum
-{
- mips_foreach_cop1x_funct
-} mips_insn_cop1x_funct_t;
-#undef _
-
-#define _(f) MIPS_MDMX_FUNCT_##f,
-typedef enum
-{
- mips_foreach_mdmx_funct
-} mips_insn_mdmx_funct_t;
-#undef _
-
-always_inline mips_insn_opcode_t
-mips_insn_get_op (u32 insn)
-{
- return (insn >> 26) & 0x3f;
-}
-
-always_inline u32
-mips_insn_get_rs (u32 insn)
-{
- return (insn >> 21) & 0x1f;
-}
-
-always_inline u32
-mips_insn_get_rt (u32 insn)
-{
- return (insn >> 16) & 0x1f;
-}
-
-always_inline u32
-mips_insn_get_rd (u32 insn)
-{
- return (insn >> 11) & 0x1f;
-}
-
-always_inline u32
-mips_insn_get_sa (u32 insn)
-{
- return (insn >> 6) & 0x1f;
-}
-
-always_inline u32
-mips_insn_get_funct (u32 insn)
-{
- return (insn >> 0) & 0x3f;
-}
-
-always_inline i32
-mips_insn_get_immediate (u32 insn)
-{
- return (((i32) insn) << 16) >> 16;
-}
-
-always_inline u32
-mips_insn_encode_i_type (int op, int rs, int rt, int immediate)
-{
- u32 insn;
- insn = immediate;
- insn |= rt << 16;
- insn |= rs << 21;
- insn |= op << 26;
-
- ASSERT (mips_insn_get_immediate (insn) == immediate);
- ASSERT (mips_insn_get_rt (insn) == rt);
- ASSERT (mips_insn_get_rs (insn) == rt);
- ASSERT (mips_insn_get_op (insn) == op);
-
- return insn;
-}
-
-always_inline u32
-mips_insn_encode_j_type (int op, u32 addr)
-{
- u32 insn;
-
- insn = (addr & ((1 << 28) - 1)) / 4;
- insn |= op << 26;
-
- return insn;
-}
-
-always_inline u32
-mips_insn_encode_r_type (int op, int rs, int rt, int rd, int sa, int funct)
-{
- u32 insn;
- insn = funct;
- insn |= sa << 6;
- insn |= rd << 11;
- insn |= rt << 16;
- insn |= rs << 21;
- insn |= op << 26;
-
- ASSERT (mips_insn_get_funct (insn) == funct);
- ASSERT (mips_insn_get_sa (insn) == sa);
- ASSERT (mips_insn_get_rd (insn) == rd);
- ASSERT (mips_insn_get_rt (insn) == rt);
- ASSERT (mips_insn_get_rs (insn) == rt);
- ASSERT (mips_insn_get_op (insn) == op);
-
- return insn;
-}
-
-#define mips_insn_r(op,funct,rd,rs,rt,sa) \
- mips_insn_encode_r_type (MIPS_OPCODE_##op, \
- (rs), (rt), (rd), (sa), \
- MIPS_##op##_FUNCT_##funct)
-
-#define mips_insn_i(op,rs,rt,imm) \
- mips_insn_encode_i_type (MIPS_OPCODE_##op, (rs), (rt), (imm))
-
-#define mips_insn_j(op,target) \
- mips_insn_encode_i_type (MIPS_OPCODE_##op, (rs), (rt), (imm))
-
-/* Generate unsigned load instructions of data of various sizes. */
-always_inline u32
-mips_insn_load (u32 rd, i32 offset, u32 base, u32 log2_bytes)
-{
- int op;
-
- ASSERT (log2_bytes < 4);
- switch (log2_bytes)
- {
- case 0:
- op = MIPS_OPCODE_lbu;
- break;
- case 1:
- op = MIPS_OPCODE_lhu;
- break;
- case 2:
- op = MIPS_OPCODE_lwu;
- break;
- case 3:
- op = MIPS_OPCODE_ld;
- break;
- }
-
- return mips_insn_encode_i_type (op, base, rd, offset);
-}
-
-typedef enum
-{
- MIPS_REG_SP = 29,
- MIPS_REG_RA = 31,
-} mips_reg_t;
-
-#endif /* included_asm_mips_h */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vppinfra/asm_x86.c b/src/vppinfra/asm_x86.c
deleted file mode 100644
index e6e00ce5543..00000000000
--- a/src/vppinfra/asm_x86.c
+++ /dev/null
@@ -1,1947 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/* FIXME
- opcode name remove to save table space; enum
- x87
- 3dnow
- cbw naming
-*/
-
-#include <vppinfra/error.h>
-#include <vppinfra/byte_order.h>
-#include <vppinfra/asm_x86.h>
-
-#define foreach_x86_gp_register \
- _ (AX) _ (CX) _ (DX) _ (BX) \
- _ (SP) _ (BP) _ (SI) _ (DI)
-
-typedef enum {
-#define _(r) X86_INSN_GP_REG_##r,
- foreach_x86_gp_register
-#undef _
-} x86_insn_gp_register_t;
-
-typedef union {
- struct {
- u8 rm : 3;
- u8 reg : 3;
- u8 mode : 2;
- };
- u8 byte;
-} x86_insn_modrm_byte_t;
-
-typedef union {
- struct {
- u8 base : 3;
- u8 index : 3;
- u8 log2_scale : 2;
- };
- u8 byte;
-} x86_insn_sib_byte_t;
-
-always_inline uword
-x86_insn_has_modrm_byte (x86_insn_t * insn)
-{
- int i;
- for (i = 0; i < ARRAY_LEN (insn->operands); i++)
- switch (insn->operands[i].code)
- {
- case 'G': case 'E': case 'M': case 'R':
- return 1;
- }
- return 0;
-}
-
-always_inline uword
-x86_insn_immediate_type (x86_insn_t * insn)
-{
- int i;
- for (i = 0; i < ARRAY_LEN (insn->operands); i++)
- switch (insn->operands[i].code)
- {
- case 'J':
- case 'I':
- case 'O':
- return insn->operands[i].type;
- }
- return 0;
-}
-
-/* Opcode extension in modrm byte reg field. */
-#define foreach_x86_insn_modrm_reg_group \
- _ (1) _ (1a) _ (2) _ (3) _ (4) _ (5) _ (6) _ (7) \
- _ (8) _ (9) _ (10) _ (11) _ (12) _ (13) _ (14) \
- _ (15) _ (16) _ (p)
-
-#define foreach_x86_insn_sse_group \
- _ (10) _ (28) _ (50) _ (58) _ (60) _ (68) _ (70) _ (78) \
- _ (c0) _ (d0) _ (d8) _ (e0) _ (e8) _ (f0) _ (f8)
-
-enum {
-#define _(x) X86_INSN_MODRM_REG_GROUP_##x,
- foreach_x86_insn_modrm_reg_group
-#undef _
-#define _(x) X86_INSN_SSE_GROUP_##x,
- foreach_x86_insn_sse_group
-#undef _
-};
-
-enum {
-#define _(x) \
- X86_INSN_FLAG_MODRM_REG_GROUP_##x \
- = X86_INSN_FLAG_SET_MODRM_REG_GROUP (1 + X86_INSN_MODRM_REG_GROUP_##x),
- foreach_x86_insn_modrm_reg_group
-#undef _
-
-#define _(x) \
- X86_INSN_FLAG_SSE_GROUP_##x \
- = X86_INSN_FLAG_SET_SSE_GROUP (1 + X86_INSN_SSE_GROUP_##x),
- foreach_x86_insn_sse_group
-#undef _
-};
-
-#define foreach_x86_gp_reg \
- _ (AX) _ (CX) _ (DX) _ (BX) \
- _ (SP) _ (BP) _ (SI) _ (DI)
-
-#define foreach_x86_condition \
- _ (o) _ (no) _ (b) _ (nb) \
- _ (z) _ (nz) _ (be) _ (nbe) \
- _ (s) _ (ns) _ (p) _ (np) \
- _ (l) _ (nl) _ (le) _ (nle)
-
-#define _3f(x,f,o0,o1,o2) \
-{ \
- .name = #x, \
- .flags = (f), \
- .operands[0] = { .data = #o0 }, \
- .operands[1] = { .data = #o1 }, \
- .operands[2] = { .data = #o2 }, \
-}
-
-#define _2f(x,f,o0,o1) _3f(x,f,o0,o1,__)
-#define _1f(x,f,o0) _2f(x,f,o0,__)
-#define _0f(x,f) _1f(x,f,__)
-
-#define _3(x,o0,o1,o2) _3f(x,0,o0,o1,o2)
-#define _2(x,o0,o1) _2f(x,0,o0,o1)
-#define _1(x,o0) _1f(x,0,o0)
-#define _0(x) _0f(x,0)
-
-static x86_insn_t x86_insns_one_byte[256] = {
-
-#define _(x) \
- _2 (x, Eb, Gb), \
- _2 (x, Ev, Gv), \
- _2 (x, Gb, Eb), \
- _2 (x, Gv, Ev), \
- _2 (x, AL, Ib), \
- _2 (x, AX, Iz)
-
- /* 0x00 */
- _ (add),
- _0 (push_es),
- _0 (pop_es),
- _ (or),
- _0 (push_cs),
- _0 (escape_two_byte),
-
- /* 0x10 */
- _ (adc),
- _0 (push_ss),
- _0 (pop_ss),
- _ (sbb),
- _0 (push_ds),
- _0 (pop_ds),
-
- /* 0x20 */
- _ (and),
- _0 (segment_es),
- _0 (daa),
- _ (sub),
- _0 (segment_cs),
- _0 (das),
-
- /* 0x30 */
- _ (xor),
- _0 (segment_ss),
- _0 (aaa),
- _ (cmp),
- _0 (segment_ds),
- _0 (aas),
-
-#undef _
-
- /* 0x40 */
-#define _(r) _1 (inc, r),
- foreach_x86_gp_reg
-#undef _
-#define _(r) _1 (dec, r),
- foreach_x86_gp_reg
-#undef _
-
- /* 0x50 */
-#define _(r) _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, r),
- foreach_x86_gp_reg
-#undef _
-#define _(r) _1f (pop, X86_INSN_FLAG_DEFAULT_64_BIT, r),
- foreach_x86_gp_reg
-#undef _
-
- /* 0x60 */
- _0 (pusha),
- _0 (popa),
- _2 (bound, Gv, Ma),
- _2 (movsxd, Gv, Ed),
- _0 (segment_fs),
- _0 (segment_gs),
- _0 (operand_type),
- _0 (address_size),
- _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Iz),
- _3 (imul, Gv, Ev, Iz),
- _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ib),
- _3 (imul, Gv, Ev, Ib),
- _1 (insb, DX),
- _1 (insw, DX),
- _1 (outsb, DX),
- _1 (outsw, DX),
-
- /* 0x70 */
-#define _(x) _1 (j##x, Jb),
- foreach_x86_condition
-#undef _
-
- /* 0x80 */
- _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib),
- _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Iz),
- _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib),
- _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Ib),
- _2 (test, Eb, Gb),
- _2 (test, Ev, Gv),
- _2 (xchg, Eb, Gb),
- _2 (xchg, Ev, Gv),
- _2 (mov, Eb, Gb),
- _2 (mov, Ev, Gv),
- _2 (mov, Gb, Eb),
- _2 (mov, Gv, Ev),
- _2 (mov, Ev, Sw),
- _2 (lea, Gv, Ev),
- _2 (mov, Sw, Ew),
- _1f (modrm_group_1a, X86_INSN_FLAG_MODRM_REG_GROUP_1a, Ev),
-
- /* 0x90 */
- _0 (nop),
- _1 (xchg, CX),
- _1 (xchg, DX),
- _1 (xchg, BX),
- _1 (xchg, SP),
- _1 (xchg, BP),
- _1 (xchg, SI),
- _1 (xchg, DI),
- _0 (cbw),
- _0 (cwd),
- _1 (call, Ap),
- _0 (wait),
- _0 (pushf),
- _0 (popf),
- _0 (sahf),
- _0 (lahf),
-
- /* 0xa0 */
- _2 (mov, AL, Ob),
- _2 (mov, AX, Ov),
- _2 (mov, Ob, AL),
- _2 (mov, Ov, AX),
- _0 (movsb),
- _0 (movsw),
- _0 (cmpsb),
- _0 (cmpsw),
- _2 (test, AL, Ib),
- _2 (test, AX, Iz),
- _1 (stosb, AL),
- _1 (stosw, AX),
- _1 (lodsb, AL),
- _1 (lodsw, AX),
- _1 (scasb, AL),
- _1 (scasw, AX),
-
- /* 0xb0 */
- _2 (mov, AL, Ib),
- _2 (mov, CL, Ib),
- _2 (mov, DL, Ib),
- _2 (mov, BL, Ib),
- _2 (mov, AH, Ib),
- _2 (mov, CH, Ib),
- _2 (mov, DH, Ib),
- _2 (mov, BH, Ib),
-#define _(r) _2 (mov, r, Iv),
- foreach_x86_gp_reg
-#undef _
-
- /* 0xc0 */
- _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, Ib),
- _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, Ib),
- _1 (ret, Iw),
- _0 (ret),
- _2 (les, Gz, Mp),
- _2 (lds, Gz, Mp),
- _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Eb, Ib),
- _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Ev, Iz),
- _2 (enter, Iw, Ib),
- _0 (leave),
- _1 (ret, Iw),
- _0 (ret),
- _0 (int3),
- _1 (int, Ib),
- _0 (into),
- _0 (iret),
-
- /* 0xd0 */
- _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, 1b),
- _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, 1b),
- _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, CL),
- _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, CL),
- _0 (aam),
- _0 (aad),
- _0 (salc),
- _0 (xlat),
- /* FIXME x87 */
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
-
- /* 0xe0 */
- _1 (loopnz, Jb),
- _1 (loopz, Jb),
- _1 (loop, Jb),
- _1 (jcxz, Jb),
- _2 (in, AL, Ib),
- _2 (in, AX, Ib),
- _2 (out, Ib, AL),
- _2 (out, Ib, AX),
- _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Jz),
- _1f ( jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Jz),
- _1 (jmp, Ap),
- _1 (jmp, Jb),
- _2 (in, AL, DX),
- _2 (in, AX, DX),
- _2 (out, DX, AL),
- _2 (out, DX, AX),
-
- /* 0xf0 */
- _0 (lock),
- _0 (int1),
- _0 (repne),
- _0 (rep),
- _0 (hlt),
- _0 (cmc),
- _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3),
- _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3),
- _0 (clc),
- _0 (stc),
- _0 (cli),
- _0 (sti),
- _0 (cld),
- _0 (std),
- _1f (modrm_group_4, X86_INSN_FLAG_MODRM_REG_GROUP_4, Eb),
- _0f (modrm_group_5, X86_INSN_FLAG_MODRM_REG_GROUP_5),
-};
-
-static x86_insn_t x86_insns_two_byte[256] = {
- /* 0x00 */
- _0f (modrm_group_6, X86_INSN_FLAG_MODRM_REG_GROUP_6),
- _0f (modrm_group_7, X86_INSN_FLAG_MODRM_REG_GROUP_7),
- _2 (lar, Gv, Ew),
- _2 (lsl, Gv, Ew),
- _0 (bad),
- _0 (syscall),
- _0 (clts),
- _0 (sysret),
- _0 (invd),
- _0 (wbinvd),
- _0 (bad),
- _0 (ud2),
- _0 (bad),
- _0f (modrm_group_p, X86_INSN_FLAG_MODRM_REG_GROUP_p),
- _0 (femms),
- _0 (escape_3dnow),
-
- /* 0x10 */
- _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
- _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx),
- _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx),
- _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
- _2f (unpcklps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
- _2f (unpckhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
- _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx),
- _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
- _0f (modrm_group_16, X86_INSN_FLAG_MODRM_REG_GROUP_16),
- _0 (nop),
- _0 (nop),
- _0 (nop),
- _0 (nop),
- _0 (nop),
- _0 (nop),
- _0 (nop),
-
- /* 0x20 */
- _2 (mov, Rv, Cv),
- _2 (mov, Rv, Dv),
- _2 (mov, Cv, Rv),
- _2 (mov, Dv, Rv),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
- _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Ex, Gx),
- _2f (cvtpi2ps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
- _2f (movntps, X86_INSN_FLAG_SSE_GROUP_28, Mx, Gx),
- _2f (cvttps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
- _2f (cvtps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
- _2f (ucomiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
- _2f (comiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
-
- /* 0x30 */
- _0 (wrmsr),
- _0 (rdtsc),
- _0 (rdmsr),
- _0 (rdpmc),
- _0 (sysenter),
- _0 (sysexit),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
-
- /* 0x40 */
-#define _(x) _2 (cmov##x, Gv, Ev),
- foreach_x86_condition
-#undef _
-
- /* 0x50 */
- _2f (movmskps, X86_INSN_FLAG_SSE_GROUP_50, Gd, Rx),
- _2f (sqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (rsqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (rcpps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (andps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (andnps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (orps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (xorps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
- _2f (addps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (mulps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (cvtps2pd, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (cvtdq2ps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (subps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (minps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (divps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
- _2f (maxps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
-
- /* 0x60 */
- _2f (punpcklbw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (punpcklwd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (punpckldq, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (packsswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (pcmpgtb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (pcmpgtw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (pcmpgtd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (packuswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
- _2f (punpckhbw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
- _2f (punpckhwd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
- _2f (punpckhdq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
- _2f (packssdw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_68),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_68),
- _2f (movd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
- _2f (movq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
-
- /* 0x70 */
- _3f (pshufw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em, Ib),
- _0f (modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12),
- _0f (modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13),
- _0f (modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14),
- _2f (pcmpeqb, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em),
- _2f (pcmpeqw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em),
- _2f (pcmpeqd, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em),
- _0f (emms, X86_INSN_FLAG_SSE_GROUP_70),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
- _2f (movd, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm),
- _2f (movq, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm),
-
- /* 0x80 */
-#define _(x) _1 (jmp##x, Jz),
- foreach_x86_condition
-#undef _
-
- /* 0x90 */
-#define _(x) _1 (set##x, Eb),
- foreach_x86_condition
-#undef _
-
- /* 0xa0 */
- _0 (push_fs),
- _0 (pop_fs),
- _0 (cpuid),
- _2 (bt, Ev, Gv),
- _3 (shld, Ev, Gv, Ib),
- _3 (shld, Ev, Gv, CL),
- _0 (bad),
- _0 (bad),
- _0 (push_gs),
- _0 (pop_gs),
- _0 (rsm),
- _2 (bts, Ev, Gv),
- _3 (shrd, Ev, Gv, Ib),
- _3 (shrd, Ev, Gv, CL),
- _0f (modrm_group_15, X86_INSN_FLAG_MODRM_REG_GROUP_15),
- _2 (imul, Gv, Ev),
-
- /* 0xb0 */
- _2 (cmpxchg, Eb, Gb),
- _2 (cmpxchg, Ev, Gv),
- _2 (lss, Gz, Mp),
- _2 (btr, Ev, Gv),
- _2 (lfs, Gz, Mp),
- _2 (lgs, Gz, Mp),
- _2 (movzbl, Gv, Eb),
- _2 (movzwl, Gv, Ew),
- _0 (bad),
- _0f (modrm_group_10, X86_INSN_FLAG_MODRM_REG_GROUP_10),
- _2f (modrm_group_8, X86_INSN_FLAG_MODRM_REG_GROUP_8, Ev, Ib),
- _2 (btc, Ev, Gv),
- _2 (bsf, Gv, Ev),
- _2 (bsr, Gv, Ev),
- _2 (movsx, Gv, Eb),
- _2 (movsx, Gv, Ew),
-
- /* 0xc0 */
- _2 (xadd, Eb, Gb),
- _2 (xadd, Ev, Gv),
- _3f (cmpps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib),
- _2 (movnti, Mv, Gv),
- _3f (pinsrw, X86_INSN_FLAG_SSE_GROUP_c0, Gm, Ew, Ib),
- _3f (pextrw, X86_INSN_FLAG_SSE_GROUP_c0, Gd, Rm, Ib),
- _3f (shufps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib),
- _1f (modrm_group_9, X86_INSN_FLAG_MODRM_REG_GROUP_9, Mx),
-#define _(r) _1 (bswap, r),
- foreach_x86_gp_reg
-#undef _
-
- /* 0xd0 */
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_d0),
- _2f (psrlw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
- _2f (psrld, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
- _2f (psrlq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
- _2f (paddq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
- _2f (pmullw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_d0),
- _2f (pmovmskb, X86_INSN_FLAG_SSE_GROUP_d0, Gd, Rm),
- _2f (psubusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (psubusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (pminub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (pand, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (paddusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (paddusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (pmaxub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
- _2f (pandn, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
-
- /* 0xe0 */
- _2f (pavgb, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (psraw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (psrad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (pavgw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (pmulhuw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (pmulhw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (bad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
- _2f (movntq, X86_INSN_FLAG_SSE_GROUP_e0, Mm, Gm),
- _2f (psubsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (psubsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (pminsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (por, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (paddsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (paddsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (pmaxsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
- _2f (pxor, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
-
- /* 0xf0 */
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_f0),
- _2f (psllw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (pslld, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (psllq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (pmuludq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (pmaddwd, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (psadbw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (maskmovq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
- _2f (psubb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _2f (psubw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _2f (psubd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _2f (psubq, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _2f (paddb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _2f (paddw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _2f (paddd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
- _0f (bad, X86_INSN_FLAG_SSE_GROUP_f8),
-};
-
-typedef struct {
- x86_insn_t insns[8];
-} x86_insn_group8_t;
-
-/* Escape groups are indexed by modrm reg field. */
-static x86_insn_group8_t x86_insn_modrm_reg_groups[] = {
- [X86_INSN_MODRM_REG_GROUP_1].insns = {
- _0 (add), _0 ( or), _0 (adc), _0 (sbb),
- _0 (and), _0 (sub), _0 (xor), _0 (cmp),
- },
-
- [X86_INSN_MODRM_REG_GROUP_1a].insns = {
- _0f (pop, X86_INSN_FLAG_DEFAULT_64_BIT),
- _0 (bad), _0 (bad), _0 (bad),
- _0 (bad), _0 (bad), _0 (bad), _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_2].insns = {
- _0 (rol), _0 (ror), _0 (rcl), _0 (rcr),
- _0 (shl), _0 (shr), _0 (sal), _0 (sar),
- },
-
- [X86_INSN_MODRM_REG_GROUP_3].insns = {
- _0 (test), _0 (test), _0 (not), _0 (neg),
- _0 (mul), _0 (imul), _0 (div), _0 (idiv),
- },
-
- [X86_INSN_MODRM_REG_GROUP_4].insns = {
- _0 (inc), _0 (dec), _0 (bad), _0 (bad),
- _0 (bad), _0 (bad), _0 (bad), _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_5].insns = {
- _1 (inc, Ev),
- _1 (dec, Ev),
- _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Ev),
- _1 (call, Mp),
- _1f (jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Ev),
- _1 (jmp, Mp),
- _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ev),
- _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_6].insns = {
- _1 (sldt, Ev),
- _1 (str, Ev),
- _1 (lldt, Ev),
- _1 (ltr, Ev),
- _1 (verr, Ev),
- _1 (verw, Ev),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_7].insns = {
- _1 (sgdt, Mv),
- _1 (sidt, Mv),
- _1 (lgdt, Mv),
- _1 (lidt, Mv),
- _1 (smsw, Ev),
- _0 (bad),
- _1 (lmsw, Ew),
- _1 (invlpg, Mv),
- },
-
- [X86_INSN_MODRM_REG_GROUP_8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (bt, Ev, Ib),
- _2 (bts, Ev, Ib),
- _2 (btr, Ev, Ib),
- _2 (btc, Ev, Ib),
- },
-
- [X86_INSN_MODRM_REG_GROUP_9].insns = {
- _0 (bad),
- _1 (cmpxchg, Mx),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_10].insns = {
- _0 (bad), _0 (bad), _0 (bad), _0 (bad),
- _0 (bad), _0 (bad), _0 (bad), _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_11].insns = {
- _0 (mov), _0 (bad), _0 (bad), _0 (bad),
- _0 (bad), _0 (bad), _0 (bad), _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_12].insns = {
- _0 (bad),
- _0 (bad),
- _2 (psrlw, Rm, Ib),
- _0 (bad),
- _2 (psraw, Rm, Ib),
- _0 (bad),
- _2 (psllw, Rm, Ib),
- _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_13].insns = {
- _0 (bad),
- _0 (bad),
- _2 (psrld, Rm, Ib),
- _0 (bad),
- _2 (psrad, Rm, Ib),
- _0 (bad),
- _2 (pslld, Rm, Ib),
- _0 (bad),
- },
-
- [X86_INSN_MODRM_REG_GROUP_14].insns = {
- _0 (bad),
- _0 (bad),
- _2 (psrlq, Rm, Ib),
- _0f (bad, 0),
- _0 (bad),
- _0 (bad),
- _2 (psllq, Rm, Ib),
- _0f (bad, 0),
- },
-
- [X86_INSN_MODRM_REG_GROUP_15].insns = {
- _1 (fxsave, Mv),
- _1 (fxrstor, Mv),
- _1 (ldmxcsr, Mv),
- _1 (stmxcsr, Mv),
- _0 (bad),
- _1 (lfence, Mv),
- _1 (mfence, Mv),
- _1 (sfence, Mv),
- },
-
- [X86_INSN_MODRM_REG_GROUP_16].insns = {
- _1 (prefetch_nta, Mv),
- _1 (prefetch_t0, Mv),
- _1 (prefetch_t1, Mv),
- _1 (prefetch_t2, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_nop, Mv),
- },
-
- [X86_INSN_MODRM_REG_GROUP_p].insns = {
- _1 (prefetch_exclusive, Mv),
- _1 (prefetch_modified, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_modified, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_nop, Mv),
- _1 (prefetch_nop, Mv),
- },
-};
-
-static x86_insn_group8_t x86_insn_sse_groups_repz[] = {
- [X86_INSN_SSE_GROUP_10].insns = {
- _2 (movss, Gx, Ex),
- _2 (movss, Ex, Gx),
- _2 (movsldup, Gx, Ex),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (movshdup, Gx, Ex),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_28].insns = {
- _0 (bad),
- _0 (bad),
- _2 (cvtsi2ss, Gx, Ev),
- _0 (bad),
- _2 (cvttss2si, Gv, Ex),
- _2 (cvtss2si, Gv, Ex),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_50].insns = {
- _0 (bad),
- _2 (sqrtss, Gx, Ex),
- _2 (rsqrtps, Gx, Ex),
- _2 (rcpss, Gx, Ex),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_58].insns = {
- _2 (addss, Gx, Ex),
- _2 (mulss, Gx, Ex),
- _2 (cvtss2sd, Gx, Ex),
- _2 (cvttps2dq, Gx, Ex),
- _2 (subss, Gx, Ex),
- _2 (minss, Gx, Ex),
- _2 (divss, Gx, Ex),
- _2 (maxss, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_60].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_68].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (movdqu, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_70].insns = {
- _3 (pshufhw, Gx, Ex, Ib),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_78].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (movq, Gx, Ex),
- _2 (movdqu, Ex, Gx),
- },
-
- [X86_INSN_SSE_GROUP_c0].insns = {
- _0 (bad),
- _0 (bad),
- _3 (cmpss, Gx, Ex, Ib),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_d0].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (movq2dq, Gx, Em),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_d8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_e0].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (cvtdq2pd, Gx, Ex),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_e8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_f0].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_f8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-};
-
-static x86_insn_group8_t x86_insn_sse_groups_operand_size[] = {
- [X86_INSN_SSE_GROUP_10].insns = {
- _2 (movupd, Gx, Ex),
- _2 (movupd, Ex, Gx),
- _2 (movlpd, Gx, Ex),
- _2 (movlpd, Ex, Gx),
- _2 (unpcklpd, Gx, Ex),
- _2 (unpckhpd, Gx, Ex),
- _2 (movhpd, Gx, Mx),
- _2 (movhpd, Mx, Gx),
- },
-
- [X86_INSN_SSE_GROUP_28].insns = {
- _2 (movapd, Gx, Ex),
- _2 (movapd, Ex, Gx),
- _2 (cvtpi2pd, Gx, Ex),
- _2 (movntpd, Mx, Gx),
- _2 (cvttpd2pi, Gx, Mx),
- _2 (cvtpd2pi, Gx, Mx),
- _2 (ucomisd, Gx, Ex),
- _2 (comisd, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_50].insns = {
- _2 (movmskpd, Gd, Rx),
- _2 (sqrtpd, Gx, Ex),
- _0 (bad),
- _0 (bad),
- _2 (andpd, Gx, Ex),
- _2 (andnpd, Gx, Ex),
- _2 (orpd, Gx, Ex),
- _2 (xorpd, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_58].insns = {
- _2 (addpd, Gx, Ex),
- _2 (mulpd, Gx, Ex),
- _2 (cvtpd2ps, Gx, Ex),
- _2 (cvtps2dq, Gx, Ex),
- _2 (subpd, Gx, Ex),
- _2 (minpd, Gx, Ex),
- _2 (divpd, Gx, Ex),
- _2 (maxpd, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_60].insns = {
- _2 (punpcklbw, Gx, Ex),
- _2 (punpcklwd, Gx, Ex),
- _2 (punpckldq, Gx, Ex),
- _2 (packsswb, Gx, Ex),
- _2 (pcmpgtb, Gx, Ex),
- _2 (pcmpgtw, Gx, Ex),
- _2 (pcmpgtd, Gx, Ex),
- _2 (packuswb, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_68].insns = {
- _2 (punpckhbw, Gx, Ex),
- _2 (punpckhwd, Gx, Ex),
- _2 (punpckhdq, Gx, Ex),
- _2 (packssdw, Gx, Ex),
- _2 (punpcklqdq, Gx, Ex),
- _2 (punpckhqdq, Gx, Ex),
- _2 (movd, Gx, Ev),
- _2 (movdqa, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_70].insns = {
- _3 (pshufd, Gx, Ex, Ib),
- _0f (modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12),
- _0f (modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13),
- _0f (modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14),
- _2 (pcmpeqb, Gx, Ex),
- _2 (pcmpeqw, Gx, Ex),
- _2 (pcmpeqd, Gx, Ex),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_78].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (haddpd, Gx, Ex),
- _2 (hsubpd, Gx, Ex),
- _2 (movd, Ev, Gx),
- _2 (movdqa, Ex, Gx),
- },
-
- [X86_INSN_SSE_GROUP_c0].insns = {
- _0 (bad),
- _0 (bad),
- _3 (cmppd, Gx, Ex, Ib),
- _0 (bad),
- _3 (pinsrw, Gx, Ew, Ib),
- _3 (pextrw, Gd, Gx, Ib),
- _3 (shufpd, Gx, Ex, Ib),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_d0].insns = {
- _2 (addsubpd, Gx, Ex),
- _2 (psrlw, Gx, Ex),
- _2 (psrld, Gx, Ex),
- _2 (psrlq, Gx, Ex),
- _2 (paddq, Gx, Ex),
- _2 (pmullw, Gx, Ex),
- _2 (movq, Ex, Gx),
- _2 (pmovmskb, Gd, Rx),
- },
-
- [X86_INSN_SSE_GROUP_d8].insns = {
- _2 (psubusb, Gx, Ex),
- _2 (psubusw, Gx, Ex),
- _2 (pminub, Gx, Ex),
- _2 (pand, Gx, Ex),
- _2 (paddusb, Gx, Ex),
- _2 (paddusw, Gx, Ex),
- _2 (pmaxub, Gx, Ex),
- _2 (pandn, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_e0].insns = {
- _2 (pavgb, Gx, Ex),
- _2 (psraw, Gx, Ex),
- _2 (psrad, Gx, Ex),
- _2 (pavgw, Gx, Ex),
- _2 (pmulhuw, Gx, Ex),
- _2 (pmulhw, Gx, Ex),
- _2 (cvttpd2dq, Gx, Ex),
- _2 (movntdq, Mx, Gx),
- },
-
- [X86_INSN_SSE_GROUP_e8].insns = {
- _2 (psubsb, Gx, Ex),
- _2 (psubsw, Gx, Ex),
- _2 (pminsw, Gx, Ex),
- _2 (por, Gx, Ex),
- _2 (paddsb, Gx, Ex),
- _2 (paddsw, Gx, Ex),
- _2 (pmaxsw, Gx, Ex),
- _2 (pxor, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_f0].insns = {
- _0 (bad),
- _2 (psllw, Gx, Ex),
- _2 (pslld, Gx, Ex),
- _2 (psllq, Gx, Ex),
- _2 (pmuludq, Gx, Ex),
- _2 (pmaddwd, Gx, Ex),
- _2 (psadbw, Gx, Ex),
- _2 (maskmovdqu, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_f8].insns = {
- _2 (psubb, Gx, Ex),
- _2 (psubw, Gx, Ex),
- _2 (psubd, Gx, Ex),
- _2 (psubq, Gx, Ex),
- _2 (paddb, Gx, Ex),
- _2 (paddw, Gx, Ex),
- _2 (paddd, Gx, Ex),
- _0 (bad),
- },
-};
-
-static x86_insn_group8_t x86_insn_sse_groups_repnz[] = {
- [X86_INSN_SSE_GROUP_10].insns = {
- _2 (movsd, Gx, Ex),
- _2 (movsd, Ex, Gx),
- _2 (movddup, Gx, Ex),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_28].insns = {
- _0 (bad),
- _0 (bad),
- _2 (cvtsi2sd, Gx, Ev),
- _0 (bad),
- _2 (cvttsd2si, Gv, Ex),
- _2 (cvtsd2si, Gv, Ex),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_50].insns = {
- _0 (bad),
- _2 (sqrtsd, Gx, Ex),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_58].insns = {
- _2 (addsd, Gx, Ex),
- _2 (mulsd, Gx, Ex),
- _2 (cvtsd2ss, Gx, Ex),
- _0 (bad),
- _2 (subsd, Gx, Ex),
- _2 (minsd, Gx, Ex),
- _2 (divsd, Gx, Ex),
- _2 (maxsd, Gx, Ex),
- },
-
- [X86_INSN_SSE_GROUP_60].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_68].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_70].insns = {
- _3 (pshuflw, Gx, Ex, Ib),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_78].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (haddps, Gx, Ex),
- _2 (hsubps, Gx, Ex),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_c0].insns = {
- _0 (bad),
- _0 (bad),
- _3 (cmpsd, Gx, Ex, Ib),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_d0].insns = {
- _2 (addsubps, Gx, Ex),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (movdq2q, Gm, Ex),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_d8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_e0].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _2 (cvtpd2dq, Gx, Ex),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_e8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_f0].insns = {
- _2 (lddqu, Gx, Mx),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-
- [X86_INSN_SSE_GROUP_f8].insns = {
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- _0 (bad),
- },
-};
-
-#undef _
-
-/* Parses memory displacements and immediates. */
-static u8 * x86_insn_parse_number (u32 log2_n_bytes,
- u8 * code, u8 * code_end,
- i64 * result)
-{
- i64 x = 0;
-
- if (code + (1 << log2_n_bytes) > code_end)
- return 0;
-
- switch (log2_n_bytes)
- {
- case 3:
- x = clib_little_to_host_unaligned_mem_u64 ((u64 *) code);
- break;
-
- case 2:
- x = (i32) clib_little_to_host_unaligned_mem_u32 ((u32 *) code);
- break;
-
- case 1:
- x = (i16) clib_little_to_host_unaligned_mem_u16 ((u16 *) code);
- break;
-
- case 0:
- x = (i8) code[0];
- break;
-
- default:
- ASSERT (0);
- }
-
- *result = x;
- return code + (1 << log2_n_bytes);
-}
-
-static u32
-x86_insn_log2_immediate_bytes (x86_insn_parse_t * p, x86_insn_t * insn)
-{
- u32 i = ~0;
- switch (x86_insn_immediate_type (insn))
- {
- case 'b': i = 0; break;
- case 'w': i = 1; break;
- case 'd': i = 2; break;
- case 'q': i = 3; break;
-
- case 'z':
- i = p->log2_effective_operand_bytes;
- if (i > 2) i = 2;
- break;
-
- case 'v':
- i = p->log2_effective_operand_bytes;
- break;
-
- default:
- i = ~0;
- break;
- }
-
- return i;
-}
-
-static u8 *
-x86_insn_parse_modrm_byte (x86_insn_parse_t * x,
- x86_insn_modrm_byte_t modrm,
- u32 parse_flags,
- u8 * code,
- u8 * code_end)
-{
- u8 effective_address_bits;
-
- if (parse_flags & X86_INSN_PARSE_64_BIT)
- effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 32 : 64;
- else if (parse_flags & X86_INSN_PARSE_32_BIT)
- effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 16 : 32;
- else
- effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 32 : 16;
-
- x->log2_effective_address_bytes = 1;
- x->log2_effective_address_bytes += effective_address_bits > 16;
- x->log2_effective_address_bytes += effective_address_bits > 32;
-
- x->regs[0] |= modrm.reg;
- if (modrm.mode == 3)
- x->regs[1] |= modrm.rm;
- else
- {
- u32 log2_disp_bytes = ~0;
-
- x->flags |= X86_INSN_IS_ADDRESS;
-
- if (effective_address_bits != 16)
- {
- u8 has_sib_byte = 0;
-
- switch (modrm.mode)
- {
- case 0:
- /* When base is bp displacement is present for mode 0. */
- if (modrm.rm == X86_INSN_GP_REG_BP)
- {
- log2_disp_bytes = x->log2_effective_address_bytes;
- break;
- }
- else if (modrm.rm == X86_INSN_GP_REG_SP
- && effective_address_bits != 16)
- {
- has_sib_byte = 1;
- break;
- }
- /* fall through */
- case 1:
- case 2:
- x->regs[1] |= modrm.rm;
- x->flags |= X86_INSN_HAS_BASE;
- if (modrm.mode != 0)
- {
- log2_disp_bytes = (modrm.mode == 1
- ? 0
- : x->log2_effective_address_bytes);
- if (log2_disp_bytes > 2)
- log2_disp_bytes = 2;
- }
- break;
- }
-
- if (has_sib_byte)
- {
- x86_insn_sib_byte_t sib;
-
- if (code >= code_end)
- return 0;
- sib.byte = *code++;
-
- x->log2_index_scale = 1 << sib.log2_scale;
- x->regs[1] |= sib.base;
- x->flags |= X86_INSN_HAS_BASE;
-
- if (sib.index != X86_INSN_GP_REG_SP)
- {
- x->regs[2] |= sib.index;
- x->flags |= X86_INSN_HAS_INDEX;
- }
- }
- }
- else
- {
- /* effective_address_bits == 16 */
- switch (modrm.mode)
- {
- case 0:
- if (modrm.rm == 6)
- {
- /* [disp16] */
- log2_disp_bytes = 1;
- break;
- }
- /* fall through */
- case 1:
- case 2:
- switch (modrm.rm)
- {
- case 0: /* [bx + si/di] */
- case 1:
- x->regs[1] = X86_INSN_GP_REG_BX;
- x->regs[2] = X86_INSN_GP_REG_SI + (modrm.rm & 1);
- x->flags |= X86_INSN_HAS_BASE | X86_INSN_HAS_INDEX;
- break;
-
- case 2: /* [bp + si/di] */
- case 3:
- x->regs[1] = X86_INSN_GP_REG_BP;
- x->regs[2] = X86_INSN_GP_REG_SI + (modrm.rm & 1);
- x->flags |= X86_INSN_HAS_BASE | X86_INSN_HAS_INDEX;
- break;
-
- case 4: /* [si/di] */
- case 5:
- x->regs[1] = X86_INSN_GP_REG_SI + (modrm.rm & 1);
- x->flags |= X86_INSN_HAS_BASE;
- break;
-
- case 6: /* [bp + disp] */
- x->regs[1] = X86_INSN_GP_REG_BP;
- x->flags |= X86_INSN_HAS_BASE;
- break;
-
- case 7: /* [bx + disp] */
- x->regs[1] = X86_INSN_GP_REG_BX;
- x->flags |= X86_INSN_HAS_BASE;
- break;
- }
-
- if (modrm.mode != 0)
- log2_disp_bytes = modrm.mode == 1 ? 0 : 1;
- break;
- }
- }
-
- if (log2_disp_bytes != ~0)
- {
- i64 disp;
- code = x86_insn_parse_number (log2_disp_bytes, code, code_end,
- &disp);
- if (code)
- x->displacement = disp;
- }
- }
-
- return code;
-}
-
-u8 * x86_insn_parse (x86_insn_parse_t * p, u8 * code_start)
-{
- u8 i, * code, * code_end;
- x86_insn_t * insn, * group_insn;
- u8 default_operand_bits, effective_operand_bits;
- u32 opcode, parse_flags;
-
- /* Preserve global parse flags. */
- parse_flags = p->flags & (X86_INSN_PARSE_32_BIT | X86_INSN_PARSE_64_BIT);
- clib_memset (p, 0, sizeof (p[0]));
- p->flags = parse_flags;
-
- /* 64 implies 32 bit parsing. */
- if (parse_flags & X86_INSN_PARSE_64_BIT)
- parse_flags |= X86_INSN_PARSE_32_BIT;
-
- /* Instruction must be <= 15 bytes. */
- code = code_start;
- code_end = code + 15;
-
- /* Parse legacy prefixes. */
- while (1)
- {
- if (code >= code_end)
- goto insn_too_long;
- i = code[0];
- code++;
- switch (i)
- {
- default: goto prefix_done;
-
- /* Set flags based on prefix. */
-#define _(x,o) case o: p->flags |= X86_INSN_##x; break;
- foreach_x86_legacy_prefix;
-#undef _
- }
- }
- prefix_done:
-
- /* REX prefix. */
- if ((parse_flags & X86_INSN_PARSE_64_BIT) && i >= 0x40 && i <= 0x4f)
- {
- p->regs[0] |= ((i & (1 << 2)) != 0) << 3; /* r bit */
- p->regs[1] |= ((i & (1 << 0)) != 0) << 3; /* b bit */
- p->regs[2] |= ((i & (1 << 1)) != 0) << 3; /* x bit */
- p->flags |= ((i & (1 << 3)) /* w bit */
- ? X86_INSN_OPERAND_SIZE_64 : 0);
- if (code >= code_end)
- goto insn_too_long;
- i = *code++;
- }
-
- opcode = i;
- if (opcode == 0x0f)
- {
- /* two byte opcode. */;
- if (code >= code_end)
- goto insn_too_long;
- i = *code++;
- opcode = (opcode << 8) | i;
- insn = x86_insns_two_byte + i;
- }
- else
- {
- static x86_insn_t arpl = {
- .name = "arpl",
- .operands[0].data = "Ew",
- .operands[1].data = "Gw",
- };
-
- if (PREDICT_FALSE (i == 0x63
- && ! (parse_flags & X86_INSN_PARSE_64_BIT)))
- insn = &arpl;
- else
- insn = x86_insns_one_byte + i;
- }
-
- if ((i = X86_INSN_FLAG_GET_SSE_GROUP (insn->flags)) != 0)
- {
- x86_insn_group8_t * g8;
-
- if (p->flags & X86_INSN_OPERAND_SIZE)
- g8 = x86_insn_sse_groups_operand_size;
- else if (p->flags & X86_INSN_REPZ)
- g8 = x86_insn_sse_groups_repz;
- else if (p->flags & X86_INSN_REPNZ)
- g8 = x86_insn_sse_groups_repnz;
- else
- g8 = 0;
-
- /* insn flags have 1 + group so != 0 test above can work. */
- ASSERT ((i - 1) < ARRAY_LEN (x86_insn_sse_groups_operand_size));
- if (g8)
- insn = g8[i - 1].insns + (opcode & 7);
- }
-
- /* Parse modrm and displacement if present. */
- if (x86_insn_has_modrm_byte (insn))
- {
- x86_insn_modrm_byte_t modrm;
-
- if (code >= code_end)
- goto insn_too_long;
- modrm.byte = *code++;
-
- /* Handle special 0x0f01 and 0x0fae encodings. */
- if (PREDICT_FALSE (modrm.mode == 3
- && (opcode == 0x0f01
- || opcode == 0x0fae)))
- {
- static x86_insn_t x86_insns_0f01_special[] = {
- _0 (swapgs), _0 (rdtscp), _0 (bad), _0 (bad),
- _0 (bad), _0 (bad), _0 (bad), _0 (bad),
- };
- static x86_insn_t x86_insns_0fae_special[] = {
- _0 (vmrun), _0 (vmmcall), _0 (vmload), _0 (vmsave),
- _0 (stgi), _0 (clgi), _0 (skinit), _0 (invlpga),
- };
-
- if (opcode == 0x0f01)
- insn = x86_insns_0f01_special;
- else
- insn = x86_insns_0fae_special;
- insn += modrm.rm;
- opcode = (opcode << 8) | modrm.byte;
- }
- else
- {
- code = x86_insn_parse_modrm_byte (p, modrm, parse_flags,
- code, code_end);
- if (! code)
- goto insn_too_long;
- }
- }
-
- group_insn = 0;
- if ((i = X86_INSN_FLAG_GET_MODRM_REG_GROUP (insn->flags)) != 0)
- {
- u32 g = i - 1;
- ASSERT (g < ARRAY_LEN (x86_insn_modrm_reg_groups));
- group_insn = x86_insn_modrm_reg_groups[g].insns + (p->regs[0] & 7);
- }
-
- p->insn = insn[0];
- if (group_insn)
- {
- u32 k;
- p->insn.name = group_insn->name;
- p->insn.flags |= group_insn->flags;
- for (k = 0; k < ARRAY_LEN (group_insn->operands); k++)
- if (x86_insn_operand_is_valid (group_insn, k))
- p->insn.operands[k] = group_insn->operands[k];
- }
-
- default_operand_bits
- = ((((parse_flags & X86_INSN_PARSE_32_BIT) != 0)
- ^ ((p->flags & X86_INSN_OPERAND_SIZE) != 0))
- ? BITS (u32) : BITS (u16));
-
- if ((parse_flags & X86_INSN_PARSE_64_BIT)
- && (p->insn.flags & X86_INSN_FLAG_DEFAULT_64_BIT))
- default_operand_bits = BITS (u64);
-
- effective_operand_bits = default_operand_bits;
- if (p->flags & X86_INSN_OPERAND_SIZE_64)
- effective_operand_bits = BITS (u64);
-
- p->log2_effective_operand_bytes = 1;
- p->log2_effective_operand_bytes += effective_operand_bits > 16;
- p->log2_effective_operand_bytes += effective_operand_bits > 32;
-
- /* Parse immediate if present. */
- {
- u32 l = x86_insn_log2_immediate_bytes (p, insn);
- if (l <= 3)
- {
- code = x86_insn_parse_number (l, code, code_end, &p->immediate);
- if (! code)
- goto insn_too_long;
- }
- }
-
- return code;
-
- insn_too_long:
- return 0;
-}
-
-static u8 * format_x86_gp_reg_operand (u8 * s, va_list * va)
-{
- u32 r = va_arg (*va, u32);
- u32 log2_n_bytes = va_arg (*va, u32);
-
- const char names8[8] = "acdbsbsd";
- const char names16[8] = "xxxxppii";
-
- ASSERT (r < 16);
-
- /* Add % register prefix. */
- vec_add1 (s, '%');
-
- switch (log2_n_bytes)
- {
- case 0:
- {
-
- if (r < 8)
- s = format (s, "%c%c", names8[r & 3], (r >> 2) ? 'l' : 'h');
- else
- s = format (s, "r%db", r);
- }
- break;
-
- case 2:
- case 3:
- s = format (s, "%c", log2_n_bytes == 2 ? 'e' : 'r');
- /* fall through */
- case 1:
- if (r < 8)
- s = format (s, "%c%c", names8[r], names16[r]);
- else
- {
- s = format (s, "%d", r);
- if (log2_n_bytes != 3)
- s = format (s, "%c", log2_n_bytes == 1 ? 'w' : 'd');
- }
- break;
-
- default:
- ASSERT (0);
- }
-
- return s;
-}
-
-static u8 * format_x86_reg_operand (u8 * s, va_list * va)
-{
- u32 reg = va_arg (*va, u32);
- u32 log2_n_bytes = va_arg (*va, u32);
- u32 type = va_arg (*va, u32);
-
- switch (type)
- {
- default:
- ASSERT (0);
- break;
-
- case 'x':
- ASSERT (reg < 16);
- return format (s, "%%xmm%d", reg);
-
- case 'm':
- ASSERT (reg < 8);
- return format (s, "%%mm%d", reg);
-
- /* Explicit byte/word/double-word/quad-word */
- case 'b': log2_n_bytes = 0; break;
- case 'w': log2_n_bytes = 1; break;
- case 'd': log2_n_bytes = 2; break;
- case 'q': log2_n_bytes = 3; break;
-
- /* Use effective operand size. */
- case 'v': break;
-
- /* word or double-word depending on effective operand size. */
- case 'z':
- log2_n_bytes = clib_min (log2_n_bytes, 2);
- break;
- }
-
- s = format (s, "%U", format_x86_gp_reg_operand, reg, log2_n_bytes);
- return s;
-}
-
-static u8 * format_x86_mem_operand (u8 * s, va_list * va)
-{
- x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *);
-
- if (p->displacement != 0)
- s = format (s, "0x%x", p->displacement);
-
- if (p->flags & X86_INSN_HAS_BASE)
- {
- s = format (s, "(%U",
- format_x86_gp_reg_operand, p->regs[1],
- p->log2_effective_address_bytes);
- if (p->flags & X86_INSN_HAS_INDEX)
- {
- s = format (s, ",%U",
- format_x86_gp_reg_operand, p->regs[2],
- p->log2_effective_address_bytes);
- if (p->log2_index_scale != 0)
- s = format (s, ",%d", 1 << p->log2_index_scale);
- }
- s = format (s, ")");
- }
-
- /* [RIP+disp] PC relative addressing in 64 bit mode. */
- else if (p->flags & X86_INSN_PARSE_64_BIT)
- s = format (s, "(%%rip)");
-
- return s;
-}
-
-static u8 * format_x86_insn_operand (u8 * s, va_list * va)
-{
- x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *);
- x86_insn_t * insn = &p->insn;
- u32 o = va_arg (*va, u32);
- u8 c, t;
-
- ASSERT (o < ARRAY_LEN (insn->operands));
- c = insn->operands[o].code;
- t = insn->operands[o].type;
-
- /* Register encoded in instruction. */
- if (c < 8)
- return format (s, "%U",
- format_x86_gp_reg_operand, c,
- p->log2_effective_operand_bytes);
-
- switch (c)
- {
- /* Memory or reg field from modrm byte. */
- case 'M':
- ASSERT (p->flags & X86_INSN_IS_ADDRESS);
- /* FALLTHROUGH */
- case 'E':
- if (p->flags & X86_INSN_IS_ADDRESS)
- s = format (s, "%U", format_x86_mem_operand, p);
- else
- s = format (s, "%U",
- format_x86_reg_operand, p->regs[1],
- p->log2_effective_operand_bytes, t);
- break;
-
- /* reg field from modrm byte. */
- case 'R':
- case 'G':
- s = format (s, "%U",
- format_x86_reg_operand, p->regs[0],
- p->log2_effective_operand_bytes, t);
- break;
-
- case 'I':
- {
- u32 l = x86_insn_log2_immediate_bytes (p, insn);
- i64 mask = pow2_mask (8ULL << l);
- s = format (s, "$0x%Lx", p->immediate & mask);
- }
- break;
-
- case 'J':
- if (p->immediate < 0)
- s = format (s, "- 0x%Lx", -p->immediate);
- else
- s = format (s, "+ 0x%Lx", p->immediate);
- break;
-
- case 'O':
- s = format (s, "0x%Lx", p->immediate);
- break;
-
- case 'A':
- /* AX/AL */
- s = format (s, "%U",
- format_x86_gp_reg_operand, X86_INSN_GP_REG_AX,
- t == 'L' ? 0 : p->log2_effective_operand_bytes);
- break;
-
- case 'B':
- /* BX/BL/BP */
- s = format (s, "%U",
- format_x86_gp_reg_operand,
- t == 'P' ? X86_INSN_GP_REG_BP : X86_INSN_GP_REG_BX,
- t == 'L' ? 0 : p->log2_effective_operand_bytes);
- break;
-
- case 'C':
- /* CX/CL */
- s = format (s, "%U",
- format_x86_gp_reg_operand, X86_INSN_GP_REG_CX,
- t == 'L' ? 0 : p->log2_effective_operand_bytes);
- break;
-
- case 'D':
- /* DX/DL/DI */
- s = format (s, "%U",
- format_x86_gp_reg_operand,
- t == 'I' ? X86_INSN_GP_REG_DI : X86_INSN_GP_REG_DX,
- t == 'L' ? 0 : p->log2_effective_operand_bytes);
- break;
-
- case 'S':
- /* SI/SP */
- s = format (s, "%U",
- format_x86_gp_reg_operand,
- t == 'I' ? X86_INSN_GP_REG_SI : X86_INSN_GP_REG_SP,
- p->log2_effective_operand_bytes);
- break;
-
- case '1':
- s = format (s, "1");
- break;
-
- default:
- ASSERT (0);
- }
-
- return s;
-}
-
-u8 * format_x86_insn_parse (u8 * s, va_list * va)
-{
- x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *);
- x86_insn_t * insn = &p->insn;
- u32 o, i, is_src_dst;
-
- s = format (s, "%s", insn->name);
-
- if (! x86_insn_operand_is_valid (insn, 0))
- goto done;
-
- is_src_dst = x86_insn_operand_is_valid (insn, 1);
-
- /* If instruction has immediate add suffix to opcode to
- indicate operand size. */
- if (is_src_dst)
- {
- u32 b;
-
- b = x86_insn_log2_immediate_bytes (p, insn);
- if (b < p->log2_effective_operand_bytes
- && (p->flags & X86_INSN_IS_ADDRESS))
- s = format (s, "%c", "bwlq"[b]);
- }
-
- for (i = 0; i < ARRAY_LEN (insn->operands); i++)
- {
- o = is_src_dst + i;
- if (! x86_insn_operand_is_valid (insn, o))
- break;
- s = format (s, "%s%U",
- i == 0 ? " " : ", ",
- format_x86_insn_operand, p, o);
- }
-
- if (is_src_dst)
- s = format (s, ", %U",
- format_x86_insn_operand, p, 0);
-
- done:
- return s;
-}
diff --git a/src/vppinfra/asm_x86.h b/src/vppinfra/asm_x86.h
deleted file mode 100644
index dacef61755c..00000000000
--- a/src/vppinfra/asm_x86.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef included_asm_x86_h
-#define included_asm_x86_h
-
-#include <vppinfra/format.h>
-
-typedef union
-{
- struct
- {
- u8 code;
- u8 type;
- };
- u8 data[2];
-} x86_insn_operand_t;
-
-typedef struct
-{
- /* Instruction name. */
- char *name;
-
- /* X86 instructions may have up to 3 operands. */
- x86_insn_operand_t operands[3];
-
- u16 flags;
-#define X86_INSN_FLAG_DEFAULT_64_BIT (1 << 0)
-#define X86_INSN_FLAG_SET_SSE_GROUP(n) ((n) << 5)
-#define X86_INSN_FLAG_GET_SSE_GROUP(f) (((f) >> 5) & 0x1f)
-#define X86_INSN_FLAG_SET_MODRM_REG_GROUP(n) (((n) & 0x3f) << 10)
-#define X86_INSN_FLAG_GET_MODRM_REG_GROUP(f) (((f) >> 10) & 0x3f)
-} x86_insn_t;
-
-always_inline uword
-x86_insn_operand_is_valid (x86_insn_t * i, uword o)
-{
- ASSERT (o < ARRAY_LEN (i->operands));
- return i->operands[o].code != '_';
-}
-
-#define foreach_x86_legacy_prefix \
- _ (OPERAND_SIZE, 0x66) \
- _ (ADDRESS_SIZE, 0x67) \
- _ (SEGMENT_CS, 0x2e) \
- _ (SEGMENT_DS, 0x3e) \
- _ (SEGMENT_ES, 0x26) \
- _ (SEGMENT_FS, 0x64) \
- _ (SEGMENT_GS, 0x65) \
- _ (SEGMENT_SS, 0x36) \
- _ (LOCK, 0xf0) \
- _ (REPZ, 0xf3) \
- _ (REPNZ, 0xf2)
-
-#define foreach_x86_insn_parse_flag \
- /* Parse in 32/64-bit mode. */ \
- _ (PARSE_32_BIT, 0) \
- _ (PARSE_64_BIT, 0) \
- _ (IS_ADDRESS, 0) \
- /* regs[1/2] is a valid base/index register */ \
- _ (HAS_BASE, 0) \
- _ (HAS_INDEX, 0) \
- /* rex w bit */ \
- _ (OPERAND_SIZE_64, 0)
-
-typedef enum
-{
-#define _(f,o) X86_INSN_FLAG_BIT_##f,
- foreach_x86_insn_parse_flag foreach_x86_legacy_prefix
-#undef _
-} x86_insn_parse_flag_bit_t;
-
-typedef enum
-{
-#define _(f,o) X86_INSN_##f = 1 << X86_INSN_FLAG_BIT_##f,
- foreach_x86_insn_parse_flag foreach_x86_legacy_prefix
-#undef _
-} x86_insn_parse_flag_t;
-
-typedef struct
-{
- /* Registers in instruction.
- [0] is modrm reg field
- [1] is base reg
- [2] is index reg. */
- u8 regs[3];
-
- /* Scale for index register. */
- u8 log2_index_scale:2;
- u8 log2_effective_operand_bytes:3;
- u8 log2_effective_address_bytes:3;
-
- i32 displacement;
-
- /* Parser flags: set of x86_insn_parse_flag_t enums. */
- u32 flags;
-
- i64 immediate;
-
- x86_insn_t insn;
-} x86_insn_parse_t;
-
-u8 *x86_insn_parse (x86_insn_parse_t * p, u8 * code_start);
-format_function_t format_x86_insn_parse;
-
-#endif /* included_asm_x86_h */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vppinfra/backtrace.c b/src/vppinfra/backtrace.c
deleted file mode 100644
index e713bae6876..00000000000
--- a/src/vppinfra/backtrace.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- Copyright (c) 2004 Eliot Dresselhaus
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include <vppinfra/clib.h>
-#include <vppinfra/error.h>
-
-#ifdef __mips__
-
-/* Let code below know we've defined _clib_backtrace */
-#define clib_backtrace_defined
-
-#include <vppinfra/asm_mips.h>
-
-__clib_export uword
-clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip)
-{
- u32 *pc;
- void *sp;
- uword i, saved_pc;
-
- /* Figure current PC, saved PC and stack pointer. */
- asm volatile (".set push\n"
- ".set noat\n" "move %[saved_pc], $31\n" "move %[sp], $29\n"
- /* Fetches current PC. */
- "la $at, 1f\n"
- "jalr %[pc], $at\n"
- "nop\n"
- "1:\n"
- ".set pop\n":[pc] "=r" (pc),
- [saved_pc] "=r" (saved_pc),[sp] "=r" (sp));
-
- /* Also skip current frame. */
- n_frames_to_skip += 1;
-
- for (i = 0; i < max_callers + n_frames_to_skip; i++)
- {
- mips_insn_opcode_t op;
- mips_insn_special_funct_t funct;
- i32 insn, rs, rt, rd, immediate, found_saved_pc;
- u32 *start_pc;
-
- /* Parse instructions until we reach prologue for this
- stack frame. We'll need to figure out where saved
- PC is and where previous stack frame lives. */
- start_pc = pc;
- found_saved_pc = 0;
- while (1)
- {
- insn = *--pc;
- op = mips_insn_get_op (insn);
- funct = mips_insn_get_funct (insn);
- rs = mips_insn_get_rs (insn);
- rt = mips_insn_get_rt (insn);
- rd = mips_insn_get_rd (insn);
- immediate = mips_insn_get_immediate (insn);
-
- switch (op)
- {
- default:
- break;
-
- case MIPS_OPCODE_sd:
- case MIPS_OPCODE_sw:
- /* Trace stores of return address. */
- if (rt == MIPS_REG_RA)
- {
- void *addr = sp + immediate;
-
- /* If RA is stored somewhere other than in the
- stack frame, give up. */
- if (rs != MIPS_REG_SP)
- goto backtrace_done;
-
- ASSERT (immediate % 4 == 0);
- if (op == MIPS_OPCODE_sw)
- saved_pc = ((u32 *) addr)[0];
- else
- saved_pc = ((u64 *) addr)[0];
- found_saved_pc = 1;
- }
- break;
-
- case MIPS_OPCODE_addiu:
- case MIPS_OPCODE_daddiu:
- case MIPS_OPCODE_addi:
- case MIPS_OPCODE_daddi:
- if (rt == MIPS_REG_SP)
- {
- if (rs != MIPS_REG_SP)
- goto backtrace_done;
-
- ASSERT (immediate % 4 == 0);
-
- /* Assume positive offset is part of the epilogue.
- E.g.
- jr ra
- add sp,sp,100
- */
- if (immediate > 0)
- continue;
-
- /* Negative offset means allocate stack space.
- This could either be the prologue or could be due to
- alloca. */
- sp -= immediate;
-
- /* This frame will not save RA. */
- if (i == 0)
- goto found_prologue;
-
- /* Assume that addiu sp,sp,-N without store of ra means
- that we have not found the prologue yet. */
- if (found_saved_pc)
- goto found_prologue;
- }
- break;
-
- case MIPS_OPCODE_slti:
- case MIPS_OPCODE_sltiu:
- case MIPS_OPCODE_andi:
- case MIPS_OPCODE_ori:
- case MIPS_OPCODE_xori:
- case MIPS_OPCODE_lui:
- case MIPS_OPCODE_ldl:
- case MIPS_OPCODE_ldr:
- case MIPS_OPCODE_lb:
- case MIPS_OPCODE_lh:
- case MIPS_OPCODE_lwl:
- case MIPS_OPCODE_lw:
- case MIPS_OPCODE_lbu:
- case MIPS_OPCODE_lhu:
- case MIPS_OPCODE_lwr:
- case MIPS_OPCODE_lwu:
- case MIPS_OPCODE_ld:
- /* Give up when we find anyone setting the stack pointer. */
- if (rt == MIPS_REG_SP)
- goto backtrace_done;
- break;
-
- case MIPS_OPCODE_SPECIAL:
- if (rd == MIPS_REG_SP)
- switch (funct)
- {
- default:
- /* Give up when we find anyone setting the stack pointer. */
- goto backtrace_done;
-
- case MIPS_SPECIAL_FUNCT_break:
- case MIPS_SPECIAL_FUNCT_jr:
- case MIPS_SPECIAL_FUNCT_sync:
- case MIPS_SPECIAL_FUNCT_syscall:
- case MIPS_SPECIAL_FUNCT_tge:
- case MIPS_SPECIAL_FUNCT_tgeu:
- case MIPS_SPECIAL_FUNCT_tlt:
- case MIPS_SPECIAL_FUNCT_tltu:
- case MIPS_SPECIAL_FUNCT_teq:
- case MIPS_SPECIAL_FUNCT_tne:
- /* These instructions can validly have rd == MIPS_REG_SP */
- break;
- }
- break;
- }
- }
-
- found_prologue:
- /* Check sanity of saved pc. */
- if (saved_pc & 3)
- goto backtrace_done;
- if (saved_pc == 0)
- goto backtrace_done;
-
- if (i >= n_frames_to_skip)
- callers[i - n_frames_to_skip] = saved_pc;
- pc = uword_to_pointer (saved_pc, u32 *);
- }
-
-backtrace_done:
- if (i < n_frames_to_skip)
- return 0;
- else
- return i - n_frames_to_skip;
-}
-#endif /* __mips__ */
-
-#ifndef clib_backtrace_defined
-#define clib_backtrace_defined
-
-/* use glibc backtrace for stack trace */
-#include <execinfo.h>
-
-__clib_export uword
-clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip)
-{
- int size;
- void *array[20];
- /* Also skip current frame. */
- n_frames_to_skip += 1;
-
- size = clib_min (ARRAY_LEN (array), max_callers + n_frames_to_skip);
-
- size = backtrace (array, size);
-
- uword i;
-
- for (i = 0; i < max_callers + n_frames_to_skip && i < size; i++)
- {
- if (i >= n_frames_to_skip)
- callers[i - n_frames_to_skip] = pointer_to_uword (array[i]);
- }
-
- if (i < n_frames_to_skip)
- return 0;
- else
- return i - n_frames_to_skip;
-}
-
-
-#endif /* clib_backtrace_defined */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vppinfra/cache.h b/src/vppinfra/cache.h
index 4229a068486..13778a423fd 100644
--- a/src/vppinfra/cache.h
+++ b/src/vppinfra/cache.h
@@ -68,6 +68,17 @@
#define CLIB_PREFETCH_WRITE 1
#define CLIB_PREFETCH_STORE 1 /* alias for write */
+/* locality arguments to __builtin_prefetch. */
+#define CLIB_PREFETCH_TO_STREAM 0 // NTA
+#define CLIB_PREFETCH_TO_L3 1 // T2
+#define CLIB_PREFETCH_TO_L2 2 // T1
+#define CLIB_PREFETCH_TO_L1 3 // T0
+
+#define _CLIB_TARGETED_PREFETCH(n, size, type, loc) \
+ if ((size) > (n) *CLIB_CACHE_PREFETCH_BYTES) \
+ __builtin_prefetch (_addr + (n) *CLIB_CACHE_PREFETCH_BYTES, \
+ CLIB_PREFETCH_##type, CLIB_PREFETCH_TO_##loc);
+
#define _CLIB_PREFETCH(n, size, type) \
if ((size) > (n) *CLIB_CACHE_PREFETCH_BYTES) \
__builtin_prefetch (_addr + (n) *CLIB_CACHE_PREFETCH_BYTES, \
@@ -86,6 +97,19 @@
} \
while (0)
+#define CLIB_TARGETED_PREFETCH(addr, size, type, locality) \
+ do \
+ { \
+ void *_addr = (addr); \
+ \
+ ASSERT ((size) <= 4 * CLIB_CACHE_PREFETCH_BYTES); \
+ _CLIB_TARGETED_PREFETCH (0, size, type, locality); \
+ _CLIB_TARGETED_PREFETCH (1, size, type, locality); \
+ _CLIB_TARGETED_PREFETCH (2, size, type, locality); \
+ _CLIB_TARGETED_PREFETCH (3, size, type, locality); \
+ } \
+ while (0)
+
#undef _
static_always_inline void
diff --git a/src/vppinfra/clib.h b/src/vppinfra/clib.h
index d14582492d6..75cebc65672 100644
--- a/src/vppinfra/clib.h
+++ b/src/vppinfra/clib.h
@@ -385,10 +385,6 @@ void qsort (void *base, uword n, uword size,
int (*)(const void *, const void *));
#endif
-/* Stack backtrace. */
-uword
-clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip);
-
#include <vppinfra/byte_order.h>
#endif /* included_clib_h */
diff --git a/src/vppinfra/cpu.h b/src/vppinfra/cpu.h
index 7a1b75fcf7d..b3743d4c26d 100644
--- a/src/vppinfra/cpu.h
+++ b/src/vppinfra/cpu.h
@@ -150,6 +150,7 @@ _CLIB_MARCH_FN_REGISTRATION(fn)
_ (movdir64b, 7, ecx, 28) \
_ (enqcmd, 7, ecx, 29) \
_ (avx512_fp16, 7, edx, 23) \
+ _ (aperfmperf, 0x00000006, ecx, 0) \
_ (invariant_tsc, 0x80000007, edx, 8) \
_ (monitorx, 0x80000001, ecx, 29)
diff --git a/src/vppinfra/crypto/aes_cbc.h b/src/vppinfra/crypto/aes_cbc.h
index cb3d0784051..ee9263df260 100644
--- a/src/vppinfra/crypto/aes_cbc.h
+++ b/src/vppinfra/crypto/aes_cbc.h
@@ -539,4 +539,207 @@ clib_aes256_cbc_decrypt (const aes_cbc_key_data_t *kd, const u8 *ciphertext,
clib_aes_cbc_decrypt (kd, ciphertext, len, iv, AES_KEY_256, plaintext);
}
+#if __GNUC__ > 4 && !__clang__ && CLIB_DEBUG == 0
+#pragma GCC optimize("O3")
+#endif
+
+#if defined(__VAES__) && defined(__AVX512F__)
+#define u8xN u8x64
+#define u32xN u32x16
+#define u32xN_min_scalar u32x16_min_scalar
+#define u32xN_is_all_zero u32x16_is_all_zero
+#define u32xN_splat u32x16_splat
+#elif defined(__VAES__)
+#define u8xN u8x32
+#define u32xN u32x8
+#define u32xN_min_scalar u32x8_min_scalar
+#define u32xN_is_all_zero u32x8_is_all_zero
+#define u32xN_splat u32x8_splat
+#else
+#define u8xN u8x16
+#define u32xN u32x4
+#define u32xN_min_scalar u32x4_min_scalar
+#define u32xN_is_all_zero u32x4_is_all_zero
+#define u32xN_splat u32x4_splat
+#endif
+
+static_always_inline u32
+clib_aes_cbc_encrypt_multi (aes_cbc_key_data_t **key_data,
+ const uword *key_indices, u8 **plaintext,
+ const uword *oplen, u8 **iv, aes_key_size_t ks,
+ u8 **ciphertext, uword n_ops)
+{
+ int rounds = AES_KEY_ROUNDS (ks);
+ u8 placeholder[8192];
+ u32 i, j, count, n_left = n_ops;
+ u32xN placeholder_mask = {};
+ u32xN len = {};
+ u32 key_index[4 * N_AES_LANES];
+ u8 *src[4 * N_AES_LANES] = {};
+ u8 *dst[4 * N_AES_LANES] = {};
+ u8xN r[4] = {};
+ u8xN k[15][4] = {};
+
+ for (i = 0; i < 4 * N_AES_LANES; i++)
+ key_index[i] = ~0;
+
+more:
+ for (i = 0; i < 4 * N_AES_LANES; i++)
+ if (len[i] == 0)
+ {
+ if (n_left == 0)
+ {
+ /* no more work to enqueue, so we are enqueueing placeholder buffer
+ */
+ src[i] = dst[i] = placeholder;
+ len[i] = sizeof (placeholder);
+ placeholder_mask[i] = 0;
+ }
+ else
+ {
+ u8x16 t = aes_block_load (iv[0]);
+ ((u8x16 *) r)[i] = t;
+
+ src[i] = plaintext[0];
+ dst[i] = ciphertext[0];
+ len[i] = oplen[0];
+ placeholder_mask[i] = ~0;
+ if (key_index[i] != key_indices[0])
+ {
+ aes_cbc_key_data_t *kd;
+ key_index[i] = key_indices[0];
+ kd = key_data[key_index[i]];
+ for (j = 0; j < rounds + 1; j++)
+ ((u8x16 *) k[j])[i] = kd->encrypt_key[j];
+ }
+ n_left--;
+ iv++;
+ ciphertext++;
+ plaintext++;
+ key_indices++;
+ oplen++;
+ }
+ }
+
+ count = u32xN_min_scalar (len);
+
+ ASSERT (count % 16 == 0);
+
+ for (i = 0; i < count; i += 16)
+ {
+#if defined(__VAES__) && defined(__AVX512F__)
+ r[0] = u8x64_xor3 (r[0], aes_block_load_x4 (src, i), k[0][0]);
+ r[1] = u8x64_xor3 (r[1], aes_block_load_x4 (src + 4, i), k[0][1]);
+ r[2] = u8x64_xor3 (r[2], aes_block_load_x4 (src + 8, i), k[0][2]);
+ r[3] = u8x64_xor3 (r[3], aes_block_load_x4 (src + 12, i), k[0][3]);
+
+ for (j = 1; j < rounds; j++)
+ {
+ r[0] = aes_enc_round_x4 (r[0], k[j][0]);
+ r[1] = aes_enc_round_x4 (r[1], k[j][1]);
+ r[2] = aes_enc_round_x4 (r[2], k[j][2]);
+ r[3] = aes_enc_round_x4 (r[3], k[j][3]);
+ }
+ r[0] = aes_enc_last_round_x4 (r[0], k[j][0]);
+ r[1] = aes_enc_last_round_x4 (r[1], k[j][1]);
+ r[2] = aes_enc_last_round_x4 (r[2], k[j][2]);
+ r[3] = aes_enc_last_round_x4 (r[3], k[j][3]);
+
+ aes_block_store_x4 (dst, i, r[0]);
+ aes_block_store_x4 (dst + 4, i, r[1]);
+ aes_block_store_x4 (dst + 8, i, r[2]);
+ aes_block_store_x4 (dst + 12, i, r[3]);
+#elif defined(__VAES__)
+ r[0] = u8x32_xor3 (r[0], aes_block_load_x2 (src, i), k[0][0]);
+ r[1] = u8x32_xor3 (r[1], aes_block_load_x2 (src + 2, i), k[0][1]);
+ r[2] = u8x32_xor3 (r[2], aes_block_load_x2 (src + 4, i), k[0][2]);
+ r[3] = u8x32_xor3 (r[3], aes_block_load_x2 (src + 6, i), k[0][3]);
+
+ for (j = 1; j < rounds; j++)
+ {
+ r[0] = aes_enc_round_x2 (r[0], k[j][0]);
+ r[1] = aes_enc_round_x2 (r[1], k[j][1]);
+ r[2] = aes_enc_round_x2 (r[2], k[j][2]);
+ r[3] = aes_enc_round_x2 (r[3], k[j][3]);
+ }
+ r[0] = aes_enc_last_round_x2 (r[0], k[j][0]);
+ r[1] = aes_enc_last_round_x2 (r[1], k[j][1]);
+ r[2] = aes_enc_last_round_x2 (r[2], k[j][2]);
+ r[3] = aes_enc_last_round_x2 (r[3], k[j][3]);
+
+ aes_block_store_x2 (dst, i, r[0]);
+ aes_block_store_x2 (dst + 2, i, r[1]);
+ aes_block_store_x2 (dst + 4, i, r[2]);
+ aes_block_store_x2 (dst + 6, i, r[3]);
+#else
+#if __x86_64__
+ r[0] = u8x16_xor3 (r[0], aes_block_load (src[0] + i), k[0][0]);
+ r[1] = u8x16_xor3 (r[1], aes_block_load (src[1] + i), k[0][1]);
+ r[2] = u8x16_xor3 (r[2], aes_block_load (src[2] + i), k[0][2]);
+ r[3] = u8x16_xor3 (r[3], aes_block_load (src[3] + i), k[0][3]);
+
+ for (j = 1; j < rounds; j++)
+ {
+ r[0] = aes_enc_round_x1 (r[0], k[j][0]);
+ r[1] = aes_enc_round_x1 (r[1], k[j][1]);
+ r[2] = aes_enc_round_x1 (r[2], k[j][2]);
+ r[3] = aes_enc_round_x1 (r[3], k[j][3]);
+ }
+
+ r[0] = aes_enc_last_round_x1 (r[0], k[j][0]);
+ r[1] = aes_enc_last_round_x1 (r[1], k[j][1]);
+ r[2] = aes_enc_last_round_x1 (r[2], k[j][2]);
+ r[3] = aes_enc_last_round_x1 (r[3], k[j][3]);
+
+ aes_block_store (dst[0] + i, r[0]);
+ aes_block_store (dst[1] + i, r[1]);
+ aes_block_store (dst[2] + i, r[2]);
+ aes_block_store (dst[3] + i, r[3]);
+#else
+ r[0] ^= aes_block_load (src[0] + i);
+ r[1] ^= aes_block_load (src[1] + i);
+ r[2] ^= aes_block_load (src[2] + i);
+ r[3] ^= aes_block_load (src[3] + i);
+ for (j = 0; j < rounds - 1; j++)
+ {
+ r[0] = vaesmcq_u8 (vaeseq_u8 (r[0], k[j][0]));
+ r[1] = vaesmcq_u8 (vaeseq_u8 (r[1], k[j][1]));
+ r[2] = vaesmcq_u8 (vaeseq_u8 (r[2], k[j][2]));
+ r[3] = vaesmcq_u8 (vaeseq_u8 (r[3], k[j][3]));
+ }
+ r[0] = vaeseq_u8 (r[0], k[j][0]) ^ k[rounds][0];
+ r[1] = vaeseq_u8 (r[1], k[j][1]) ^ k[rounds][1];
+ r[2] = vaeseq_u8 (r[2], k[j][2]) ^ k[rounds][2];
+ r[3] = vaeseq_u8 (r[3], k[j][3]) ^ k[rounds][3];
+ aes_block_store (dst[0] + i, r[0]);
+ aes_block_store (dst[1] + i, r[1]);
+ aes_block_store (dst[2] + i, r[2]);
+ aes_block_store (dst[3] + i, r[3]);
+#endif
+#endif
+ }
+
+ len -= u32xN_splat (count);
+
+ for (i = 0; i < 4 * N_AES_LANES; i++)
+ {
+ src[i] += count;
+ dst[i] += count;
+ }
+
+ if (n_left > 0)
+ goto more;
+
+ if (!u32xN_is_all_zero (len & placeholder_mask))
+ goto more;
+
+ return n_ops;
+}
+
+#undef u8xN
+#undef u32xN
+#undef u32xN_min_scalar
+#undef u32xN_is_all_zero
+#undef u32xN_splat
+
#endif /* __crypto_aes_cbc_h__ */
diff --git a/src/vppinfra/devicetree.c b/src/vppinfra/devicetree.c
new file mode 100644
index 00000000000..9bf8eeeac6c
--- /dev/null
+++ b/src/vppinfra/devicetree.c
@@ -0,0 +1,347 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vppinfra/clib.h>
+#include <vppinfra/devicetree.h>
+
+#ifdef __linux
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#endif
+
+static_always_inline clib_dt_node_t *
+clib_dt_node_add_child (clib_dt_main_t *dm, clib_dt_node_t *n, char *name)
+{
+ clib_dt_node_t *cn;
+
+ cn = clib_mem_alloc (sizeof (clib_dt_node_t));
+ *cn = (clib_dt_node_t){ .parent = n, .depth = n ? n->depth + 1 : 0 };
+ vec_add1 (dm->nodes, cn);
+
+ if (n == 0)
+ {
+ ASSERT (dm->root == 0);
+ dm->root = cn;
+ return cn;
+ }
+
+ vec_add1 (n->child_nodes, cn);
+ cn->path = format (0, "%v/%s", n->path, name);
+ cn->dt_main = dm;
+ hash_set_mem (dm->node_by_path, cn->path, cn);
+ if (vec_len (n->child_nodes) > 1)
+ {
+ clib_dt_node_t *prev = n->child_nodes[vec_len (n->child_nodes) - 2];
+ prev->next = cn;
+ cn->prev = prev;
+ }
+
+ return cn;
+}
+
+void
+clib_dt_main_free (clib_dt_main_t *dm)
+{
+ vec_foreach_pointer (n, dm->nodes)
+ {
+ vec_foreach_pointer (p, n->properties)
+ clib_mem_free (p);
+ vec_free (n->child_nodes);
+ vec_free (n->path);
+ vec_free (n->properties);
+ }
+
+ vec_free (dm->nodes);
+ hash_free (dm->node_by_path);
+ hash_free (dm->node_by_phandle);
+}
+
+#ifdef __linux
+__clib_export clib_error_t *
+clib_dt_read_from_sysfs (clib_dt_main_t *dm)
+{
+ DIR *dir, **dir_stack = 0;
+ struct dirent *e;
+ clib_dt_node_t *n;
+ u8 *path = 0;
+ u32 path_prefix_len;
+ clib_error_t *err = 0;
+
+ path = format (0, CLIB_DT_LINUX_PREFIX);
+ path_prefix_len = vec_len (path);
+ vec_add1 (path, 0);
+
+ dir = opendir ((char *) path);
+ if (!dir)
+ {
+ err = clib_error_return (0, "'%s' opendir failed", path);
+ goto done;
+ }
+
+ dm->node_by_path = hash_create_vec (0, sizeof (u8), sizeof (uword));
+ dm->node_by_phandle = hash_create (0, sizeof (uword));
+ vec_set_len (path, path_prefix_len);
+ n = clib_dt_node_add_child (dm, 0, 0);
+
+ while (1)
+ {
+ e = readdir (dir);
+
+ if (!e)
+ {
+ closedir (dir);
+ if (vec_len (dir_stack) == 0)
+ break;
+
+ dir = dir_stack[vec_len (dir_stack) - 1];
+ vec_pop (dir_stack);
+ n = n->parent;
+ continue;
+ }
+
+ if (e->d_type == DT_REG)
+ {
+ path = format (path, "%v/%s%c", n->path, e->d_name, 0);
+ int fd = open ((char *) path, 0);
+ if (fd >= 0)
+ {
+ struct stat st;
+ if (fstat (fd, &st) == 0)
+ {
+ u32 sz = sizeof (clib_dt_property_t) + st.st_size;
+ clib_dt_property_t *p = clib_mem_alloc (sz);
+ clib_memset (p, 0, sz);
+
+ if (read (fd, p->data, st.st_size) == st.st_size)
+ {
+ strncpy (p->name, e->d_name, sizeof (p->name));
+ p->size = st.st_size;
+ vec_add1 (n->properties, p);
+ if (strncmp ("name", p->name, 5) == 0)
+ n->name = p;
+ if ((strncmp ("phandle", p->name, 8) == 0) &&
+ (p->size == 4))
+ {
+ u32 phandle =
+ clib_net_to_host_u32 (*(u32u *) p->data);
+ hash_set (dm->node_by_phandle, phandle, n);
+ }
+ }
+ else
+ {
+ clib_mem_free (p);
+ err = clib_error_return (0, "'%s' read failed", path);
+ close (fd);
+ goto done;
+ }
+ }
+ else
+ {
+ err = clib_error_return (0, "'%s' fstat failed", path);
+ close (fd);
+ goto done;
+ }
+ close (fd);
+ }
+ else
+ {
+ err = clib_error_return (0, "'%s' open failed", path);
+ goto done;
+ }
+
+ vec_set_len (path, path_prefix_len);
+ }
+ else if (e->d_type == DT_DIR)
+ {
+ DIR *subdir;
+ if (strncmp (".", e->d_name, 2) == 0 ||
+ strncmp ("..", e->d_name, 3) == 0)
+ continue;
+
+ path = format (path, "%v/%s%c", n->path, e->d_name, 0);
+ subdir = opendir ((char *) path);
+ vec_set_len (path, path_prefix_len);
+ if (subdir)
+ {
+ vec_add1 (dir_stack, dir);
+ dir = subdir;
+ n = clib_dt_node_add_child (dm, n, e->d_name);
+ }
+ else
+ {
+ err = clib_error_return (0, "'%s' opendir failed", path);
+ goto done;
+ }
+ }
+ else
+ err =
+ clib_error_return (0, "unknown entry %s [%u]", e->d_name, e->d_type);
+ }
+
+done:
+ if (err)
+ clib_dt_main_free (dm);
+ while (vec_len (dir_stack))
+ closedir (vec_pop (dir_stack));
+ vec_free (dir_stack);
+ vec_free (path);
+ return err;
+}
+#endif
+
+clib_dt_node_t *
+clib_dt_get_child_node (clib_dt_node_t *n, char *name)
+{
+ vec_foreach_pointer (cn, n->child_nodes)
+ {
+ u8 *p = cn->path + vec_len (cn->path) - 1;
+ u32 i = 0;
+
+ while (p > cn->path && p[-1] != '/')
+ p--;
+
+ if (p[-1] != '/')
+ continue;
+
+ while (p[i] == name[i] && name[i] != 0)
+ i++;
+
+ if (name[i] != 0)
+ continue;
+
+ return cn;
+ }
+
+ return 0;
+}
+
+__clib_export clib_dt_node_t *
+clib_dt_get_node_with_path (clib_dt_main_t *dm, char *fmt, ...)
+{
+ u8 *s;
+ uword *p;
+
+ va_list va;
+ va_start (va, fmt);
+ s = va_format (0, fmt, &va);
+ va_end (va);
+
+ if (s[0] != '/')
+ return 0;
+
+ p = hash_get_mem (dm->node_by_path, s);
+ if (p)
+ return (clib_dt_node_t *) p[0];
+
+ return 0;
+}
+
+__clib_export clib_dt_property_t *
+clib_dt_get_node_property_by_name (clib_dt_node_t *n, char *name)
+{
+ vec_foreach_pointer (p, n->properties)
+ if (strncmp (name, p->name, sizeof (p->name)) == 0)
+ return p;
+ return 0;
+}
+
+__clib_export int
+clib_dt_node_is_compatible (clib_dt_node_t *n, char *comp)
+{
+ clib_dt_property_t *p;
+ char *s;
+
+ p = clib_dt_get_node_property_by_name (n, "compatible");
+
+ if (!p)
+ return 0;
+
+ s = (char *) p->data;
+ for (u32 i = 1, len = 1; i <= p->size; i++)
+ {
+ if (p->data[i - 1] == 0)
+ {
+ if (strncmp (comp, s, len) == 0)
+ return 1;
+ s = (char *) p->data + i;
+ len = 1;
+ }
+ else
+ len++;
+ }
+
+ return 0;
+}
+
+__clib_export u8 *
+format_clib_dt_property_data (u8 *s, va_list *args)
+{
+ clib_dt_property_t *p = va_arg (*args, clib_dt_property_t *);
+ u32 sz = p->size, is_printable = 0;
+ u32 n_nulls = 0;
+
+ if (sz > 2 && p->data[sz - 1] == 0 && p->data[0] != 0)
+ {
+ is_printable = 1;
+ for (u32 i = 1; i < sz - 1; i++)
+ {
+ u8 c = p->data[i];
+ if (c == 0)
+ {
+ if (p->data[i - 1] == 0)
+ {
+ is_printable = 0;
+ break;
+ }
+ n_nulls++;
+ }
+ else if ((c < 0x20) || (c > 0x7f))
+ {
+ is_printable = 0;
+ break;
+ }
+ }
+ }
+
+ if (is_printable)
+ {
+ s = format (s, "'%s'", p->data);
+ if (n_nulls)
+ {
+ for (u32 i = 2; i < p->size; i++)
+ if (((u8 *) p->data)[i - 1] == 0)
+ s = format (s, ", '%s'", ((u8 *) p->data) + i);
+ }
+ }
+ else
+ {
+ s = format (s, "< %02x", p->data[0]);
+ for (u32 i = 0; i < p->size; i++)
+ s = format (s, " %02x", p->data[i]);
+ s = format (s, " >");
+ }
+ return s;
+}
+
+__clib_export clib_dt_node_t *
+clib_dt_dereference_node (clib_dt_node_t *n, char *name)
+{
+ clib_dt_property_t *p;
+ uword *h;
+
+ p = clib_dt_get_node_property_by_name (n, name);
+ if (!p || (p->size != sizeof (u32)))
+ return 0;
+
+ h = hash_get (n->dt_main->node_by_phandle,
+ clib_net_to_host_u32 (*(u32u *) p->data));
+
+ if (h)
+ return (clib_dt_node_t *) h[0];
+
+ return 0;
+}
diff --git a/src/vppinfra/devicetree.h b/src/vppinfra/devicetree.h
new file mode 100644
index 00000000000..21c2e0f7006
--- /dev/null
+++ b/src/vppinfra/devicetree.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef CLIB_DEVICETREE_H_
+#define CLIB_DEVICETREE_H_
+
+#include <vppinfra/clib.h>
+#include <vlib/vlib.h>
+
+#ifdef __linux
+#define CLIB_DT_LINUX_PREFIX "/sys/firmware/devicetree/base"
+#endif
+
+typedef struct
+{
+ char name[32];
+ u32 size;
+ u8 data[];
+} clib_dt_property_t;
+
+typedef struct clib_dt_main clib_dt_main_t;
+
+typedef struct clib_dt_node
+{
+ u8 *path;
+ struct clib_dt_node *parent;
+ struct clib_dt_node *prev;
+ struct clib_dt_node *next;
+ struct clib_dt_node **child_nodes;
+ u8 depth;
+ clib_dt_property_t *name;
+ clib_dt_property_t **properties;
+ clib_dt_main_t *dt_main;
+} clib_dt_node_t;
+
+typedef struct clib_dt_main
+{
+ clib_dt_node_t **nodes;
+ clib_dt_node_t *root;
+ uword *node_by_path;
+ uword *node_by_phandle;
+} clib_dt_main_t;
+
+clib_dt_node_t *clib_dt_get_node_with_path (clib_dt_main_t *dm, char *fmt,
+ ...);
+clib_dt_property_t *clib_dt_get_node_property_by_name (clib_dt_node_t *,
+ char *);
+int clib_dt_node_is_compatible (clib_dt_node_t *, char *);
+clib_dt_node_t *clib_dt_dereference_node (clib_dt_node_t *, char *);
+#ifdef __linux
+clib_error_t *clib_dt_read_from_sysfs (clib_dt_main_t *dm);
+#endif
+
+format_function_t format_clib_dt_desc;
+format_function_t format_clib_dt_property_data;
+
+static_always_inline int
+clib_dt_proprerty_is_u32 (clib_dt_property_t *p)
+{
+ if (p == 0 || p->size != 4)
+ return 0;
+ return 1;
+}
+
+static_always_inline u32
+clib_dt_proprerty_get_u32 (clib_dt_property_t *p)
+{
+ return clib_net_to_host_u32 (*(u32u *) p->data);
+}
+
+#endif /* CLIB_DEVICETREE_H_ */
diff --git a/src/vppinfra/format.c b/src/vppinfra/format.c
index cf17b8a1acb..642d3e20654 100644
--- a/src/vppinfra/format.c
+++ b/src/vppinfra/format.c
@@ -833,6 +833,16 @@ done:
return s;
}
+__clib_export char *
+format_c_string (u8 *s, const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ s = va_format (s, fmt, &args);
+ va_end (args);
+ vec_add1 (s, '\0');
+ return (char *) s;
+}
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/vppinfra/format.h b/src/vppinfra/format.h
index a1a70a2d64f..14bac869f89 100644
--- a/src/vppinfra/format.h
+++ b/src/vppinfra/format.h
@@ -372,6 +372,8 @@ int test_unformat_main (unformat_input_t * input);
created circular dependency problems. */
int test_vec_main (unformat_input_t * input);
+char *format_c_string (u8 *s, const char *fmt, ...);
+
#endif /* included_format_h */
/*
diff --git a/src/vppinfra/format_ansi.h b/src/vppinfra/format_ansi.h
new file mode 100644
index 00000000000..c35406aacf7
--- /dev/null
+++ b/src/vppinfra/format_ansi.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef __FORMAT_ANSI_H__
+#define __FORMAT_ANSI_H__
+
+#define ANSI_RESET "\x1b[0m"
+#define ANSI_BOLD "\x1b[1m"
+#define ANSI_ITALIC "\x1b[3m"
+#define ANSI_UNDERLINE "\x1b[4m"
+#define ANSI_BLINK "\x1b[5m"
+#define ANSI_FG_BLACK "\x1b[30m"
+#define ANSI_FG_RED "\x1b[31m"
+#define ANSI_FG_GREEN "\x1b[32m"
+#define ANSI_FG_YELLOW "\x1b[33m"
+#define ANSI_FG_BLUE "\x1b[34m"
+#define ANSI_FG_MAGENTA "\x1b[35m"
+#define ANSI_FG_CYAN "\x1b[36m"
+#define ANSI_FG_WHITE "\x1b[37m"
+#define ANSI_FG_DEFAULT "\x1b[39m"
+#define ANSI_BG_BLACK "\x1b[40m"
+#define ANSI_BG_RED "\x1b[41m"
+#define ANSI_BG_GREEN "\x1b[42m"
+#define ANSI_BG_YELLOW "\x1b[43m"
+#define ANSI_BG_BLUE "\x1b[44m"
+#define ANSI_BG_MAGENTA "\x1b[45m"
+#define ANSI_BG_CYAN "\x1b[46m"
+#define ANSI_BG_WHITE "\x1b[47m"
+#define ANSI_BG_DEFAULT "\x1b[49m"
+#define ANSI_FG_BR_BLACK "\x1b[90m"
+#define ANSI_FG_BR_RED "\x1b[91m"
+#define ANSI_FG_BR_GREEN "\x1b[92m"
+#define ANSI_FG_BR_YELLOW "\x1b[93m"
+#define ANSI_FG_BR_BLUE "\x1b[94m"
+#define ANSI_FG_BR_MAGENTA "\x1b[95m"
+#define ANSI_FG_BR_CYAN "\x1b[96m"
+#define ANSI_FG_BR_WHITE "\x1b[97m"
+#define ANSI_BG_BR_BLACK "\x1b[100m"
+#define ANSI_BG_BR_RED "\x1b[101m"
+#define ANSI_BG_BR_GREEN "\x1b[102m"
+#define ANSI_BG_BR_YELLOW "\x1b[103m"
+#define ANSI_BG_BR_BLUE "\x1b[104m"
+#define ANSI_BG_BR_MAGENTA "\x1b[105m"
+#define ANSI_BG_BR_CYAN "\x1b[106m"
+#define ANSI_BG_BR_WHITE "\x1b[107m"
+
+#endif /* __FORMAT_ANSI_H__ */
diff --git a/src/vppinfra/heap.c b/src/vppinfra/heap.c
index 7db814200f8..9920528732d 100644
--- a/src/vppinfra/heap.c
+++ b/src/vppinfra/heap.c
@@ -680,6 +680,7 @@ debug_elt (u8 * s, void *v, word i, word n)
i = -n / 2;
for (e = e0; 1; e = heap_next (e))
{
+ s = format (s, " ");
if (heap_is_free (e))
s = format (s, "index %4d, free\n", e - h->elts);
else if (h->format_elt)
diff --git a/src/vppinfra/jsonformat.c b/src/vppinfra/jsonformat.c
index 1aa3864be04..73cb94769d8 100644
--- a/src/vppinfra/jsonformat.c
+++ b/src/vppinfra/jsonformat.c
@@ -500,12 +500,13 @@ format_vl_api_mac_address_t (u8 * s, va_list * args)
mac->bytes[0], mac->bytes[1], mac->bytes[2],
mac->bytes[3], mac->bytes[4], mac->bytes[5]);
}
-#define _(T) \
- cJSON *vl_api_ ##T## _t_tojson (vl_api_ ##T## _t *a) { \
- u8 *s = format(0, "%U", format_vl_api_ ##T## _t, a); \
- cJSON *o = cJSON_CreateString((char *)s); \
- vec_free(s); \
- return o; \
+#define _(T) \
+ cJSON *vl_api_##T##_t_tojson (vl_api_##T##_t *a) \
+ { \
+ char *s = format_c_string (0, "%U", format_vl_api_##T##_t, a, 0); \
+ cJSON *o = cJSON_CreateString (s); \
+ vec_free (s); \
+ return o; \
}
foreach_type_tojson
#undef _
diff --git a/src/vppinfra/linux/mem.c b/src/vppinfra/linux/mem.c
index 734f5c4788c..17b4412e6c9 100644
--- a/src/vppinfra/linux/mem.c
+++ b/src/vppinfra/linux/mem.c
@@ -101,11 +101,13 @@ legacy_get_log2_default_hugepage_size (void)
void
clib_mem_main_init (void)
{
+ unsigned long nodemask = 0, maxnode = CLIB_MAX_NUMAS;
+ unsigned long flags = MPOL_F_MEMS_ALLOWED;
clib_mem_main_t *mm = &clib_mem_main;
long sysconf_page_size;
uword page_size;
- void *va;
- int fd;
+ void *va = 0;
+ int fd, mode;
if (mm->log2_page_sz != CLIB_MEM_PAGE_SZ_UNKNOWN)
return;
@@ -131,23 +133,8 @@ clib_mem_main_init (void)
mm->log2_sys_default_hugepage_sz = mm->log2_default_hugepage_sz;
/* numa nodes */
- va = mmap (0, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE |
- MAP_ANONYMOUS, -1, 0);
- if (va == MAP_FAILED)
- return;
-
- if (mlock (va, page_size))
- goto done;
-
- for (int i = 0; i < CLIB_MAX_NUMAS; i++)
- {
- int status;
- if (syscall (__NR_move_pages, 0, 1, &va, &i, &status, 0) == 0)
- mm->numa_node_bitmap |= 1ULL << i;
- }
-
-done:
- munmap (va, page_size);
+ if (syscall (__NR_get_mempolicy, &mode, &nodemask, maxnode, va, flags) == 0)
+ mm->numa_node_bitmap = nodemask;
}
__clib_export u64
@@ -530,6 +517,7 @@ clib_mem_get_page_stats (void *start, clib_mem_page_sz_t log2_page_size,
{
int i, *status = 0;
void **ptr = 0;
+ unsigned char incore;
log2_page_size = clib_mem_log2_page_size_validate (log2_page_size);
@@ -551,6 +539,19 @@ clib_mem_get_page_stats (void *start, clib_mem_page_sz_t log2_page_size,
for (i = 0; i < n_pages; i++)
{
+ /* move_pages() returns -ENONET in status for huge pages on 5.19+ kernel.
+ * Retry with get_mempolicy() to obtain NUMA node info only if the pages
+ * are allocated and in memory, which is checked by mincore(). */
+ if (status[i] == -ENOENT &&
+ syscall (__NR_mincore, ptr[i], 1, &incore) == 0 && (incore & 1) != 0)
+ {
+ if (syscall (__NR_get_mempolicy, &status[i], 0, 0, ptr[i],
+ MPOL_F_NODE | MPOL_F_ADDR) != 0)
+ {
+ /* if get_mempolicy fails, keep the original value in status */
+ status[i] = -ENONET;
+ }
+ }
if (status[i] >= 0 && status[i] < CLIB_MAX_NUMAS)
{
stats->mapped++;
diff --git a/src/vppinfra/mem.h b/src/vppinfra/mem.h
index 75015d59a4a..ab9c5da30ec 100644
--- a/src/vppinfra/mem.h
+++ b/src/vppinfra/mem.h
@@ -299,10 +299,27 @@ void *clib_mem_init_thread_safe (void *memory, uword memory_size);
void clib_mem_exit (void);
+typedef struct
+{
+ /* Address of callers: outer first, inner last. */
+ uword callers[12];
+
+ /* Count of allocations with this traceback. */
+ u32 n_allocations;
+
+ /* Count of bytes allocated with this traceback. */
+ u32 n_bytes;
+
+ /* Offset of this item */
+ uword offset;
+} mheap_trace_t;
+
void clib_mem_trace (int enable);
int clib_mem_is_traced (void);
+mheap_trace_t *clib_mem_trace_dup (clib_mem_heap_t *heap);
+
typedef struct
{
/* Total number of objects allocated. */
diff --git a/src/vppinfra/mem_dlmalloc.c b/src/vppinfra/mem_dlmalloc.c
index a188164a7ba..d5ff21e58c0 100644
--- a/src/vppinfra/mem_dlmalloc.c
+++ b/src/vppinfra/mem_dlmalloc.c
@@ -19,21 +19,7 @@
#include <vppinfra/lock.h>
#include <vppinfra/hash.h>
#include <vppinfra/elf_clib.h>
-
-typedef struct
-{
- /* Address of callers: outer first, inner last. */
- uword callers[12];
-
- /* Count of allocations with this traceback. */
- u32 n_allocations;
-
- /* Count of bytes allocated with this traceback. */
- u32 n_bytes;
-
- /* Offset of this item */
- uword offset;
-} mheap_trace_t;
+#include <vppinfra/stack.h>
typedef struct
{
@@ -65,15 +51,13 @@ mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset,
{
mheap_trace_main_t *tm = &mheap_trace_main;
mheap_trace_t *t;
- uword i, n_callers, trace_index, *p;
- mheap_trace_t trace;
+ uword i, trace_index, *p;
+ mheap_trace_t trace = {};
+ int index;
if (heap != tm->current_traced_mheap || mheap_trace_thread_disable)
return;
- /* Spurious Coverity warnings be gone. */
- clib_memset (&trace, 0, sizeof (trace));
-
clib_spinlock_lock (&tm->lock);
/* heap could have changed while we were waiting on the lock */
@@ -83,9 +67,19 @@ mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset,
/* Turn off tracing for this thread to avoid embarrassment... */
mheap_trace_thread_disable = 1;
- /* Skip our frame and mspace_get_aligned's frame */
- n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers), 2);
- if (n_callers == 0)
+ index = -2; /* skip first 2 stack frames */
+ foreach_clib_stack_frame (sf)
+ {
+ if (index >= 0)
+ {
+ if (index == ARRAY_LEN (trace.callers))
+ break;
+ trace.callers[index] = sf->ip;
+ }
+ index++;
+ }
+
+ if (index < 1)
goto out;
if (!tm->trace_by_callers)
@@ -565,6 +559,23 @@ clib_mem_trace_enable_disable (uword enable)
return rv;
}
+__clib_export mheap_trace_t *
+clib_mem_trace_dup (clib_mem_heap_t *heap)
+{
+ mheap_trace_main_t *tm = &mheap_trace_main;
+ mheap_trace_t *traces_copy = 0;
+
+ clib_spinlock_lock (&tm->lock);
+ if (vec_len (tm->traces) > 0 && heap == tm->current_traced_mheap)
+ {
+ traces_copy = vec_dup (tm->traces);
+ qsort (traces_copy, vec_len (traces_copy), sizeof (traces_copy[0]),
+ mheap_trace_sort);
+ }
+ clib_spinlock_unlock (&tm->lock);
+ return traces_copy;
+}
+
__clib_export clib_mem_heap_t *
clib_mem_create_heap (void *base, uword size, int is_locked, char *fmt, ...)
{
diff --git a/src/vppinfra/mhash.c b/src/vppinfra/mhash.c
index f0f1aa470d7..babaaeec726 100644
--- a/src/vppinfra/mhash.c
+++ b/src/vppinfra/mhash.c
@@ -164,6 +164,8 @@ mhash_sanitize_hash_user (mhash_t * mh)
h->user = pointer_to_uword (mh);
}
+static u8 *mhash_format_pair_default (u8 *s, va_list *args);
+
__clib_export void
mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes)
{
@@ -208,12 +210,12 @@ mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes)
vec_validate (h->key_tmps, os_get_nthreads () - 1);
ASSERT (n_key_bytes < ARRAY_LEN (t));
- h->hash = hash_create2 ( /* elts */ 0,
+ h->hash = hash_create2 (/* elts */ 0,
/* user */ pointer_to_uword (h),
/* value_bytes */ n_value_bytes,
t[n_key_bytes].key_sum, t[n_key_bytes].key_equal,
/* format pair/arg */
- 0, 0);
+ mhash_format_pair_default, 0);
}
static uword
@@ -331,8 +333,8 @@ mhash_set_mem (mhash_t * h, void *key, uword * new_value, uword * old_value)
{
if (key_alloc_from_free_list)
{
- h->key_vector_free_indices[l] = i;
- vec_set_len (h->key_vector_free_indices, l + 1);
+ vec_set_len (h->key_vector_free_indices, l);
+ h->key_vector_free_indices[l - 1] = i;
}
else
vec_dec_len (h->key_vector_or_heap, h->n_key_bytes);
@@ -371,8 +373,8 @@ mhash_unset (mhash_t * h, void *key, uword * old_value)
return 1;
}
-u8 *
-format_mhash_key (u8 * s, va_list * va)
+__clib_export u8 *
+format_mhash_key (u8 *s, va_list *va)
{
mhash_t *h = va_arg (*va, mhash_t *);
u32 ki = va_arg (*va, u32);
@@ -387,7 +389,43 @@ format_mhash_key (u8 * s, va_list * va)
else if (h->format_key)
s = format (s, "%U", h->format_key, k);
else
- s = format (s, "%U", format_hex_bytes, k, h->n_key_bytes);
+ s = format (s, "0x%U", format_hex_bytes, k, h->n_key_bytes);
+
+ return s;
+}
+
+static u8 *
+mhash_format_pair_default (u8 *s, va_list *args)
+{
+ void *CLIB_UNUSED (user_arg) = va_arg (*args, void *);
+ void *v = va_arg (*args, void *);
+ hash_pair_t *p = va_arg (*args, hash_pair_t *);
+ hash_t *h = hash_header (v);
+ mhash_t *mh = uword_to_pointer (h->user, mhash_t *);
+
+ s = format (s, "%U", format_mhash_key, mh, (u32) p->key);
+ if (hash_value_bytes (h) > 0)
+ s = format (s, " -> 0x%8U", format_hex_bytes, &p->value[0],
+ hash_value_bytes (h));
+ return s;
+}
+
+__clib_export u8 *
+format_mhash (u8 *s, va_list *va)
+{
+ mhash_t *h = va_arg (*va, mhash_t *);
+ int verbose = va_arg (*va, int);
+
+ s = format (s, "mhash %p, %wd elts, \n", h, mhash_elts (h));
+ if (mhash_key_vector_is_heap (h))
+ s = format (s, " %U", format_heap, h->key_vector_or_heap, verbose);
+ else
+ s = format (s, " keys %wd elts, %wd size, %wd free, %wd bytes used\n",
+ vec_len (h->key_vector_or_heap) / h->n_key_bytes,
+ h->n_key_bytes, vec_len (h->key_vector_free_indices),
+ vec_bytes (h->key_vector_or_heap) +
+ vec_bytes (h->key_vector_free_indices));
+ s = format (s, " %U", format_hash, h->hash, verbose);
return s;
}
diff --git a/src/vppinfra/mhash.h b/src/vppinfra/mhash.h
index 7eb1918384e..62aee365fa3 100644
--- a/src/vppinfra/mhash.h
+++ b/src/vppinfra/mhash.h
@@ -166,8 +166,13 @@ do { \
})); \
} while (0)
+u8 *format_mhash (u8 *s, va_list *va);
+
format_function_t format_mhash_key;
+/* Main test routine. */
+int test_mhash_main (unformat_input_t *input);
+
#endif /* included_clib_mhash_h */
/*
diff --git a/src/vppinfra/stack.c b/src/vppinfra/stack.c
new file mode 100644
index 00000000000..190e880c228
--- /dev/null
+++ b/src/vppinfra/stack.c
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+
+#include <vppinfra/clib.h>
+#include <vppinfra/stack.h>
+#include <vppinfra/error.h>
+
+#if HAVE_LIBUNWIND == 1
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+static __thread unw_cursor_t cursor;
+static __thread unw_context_t context;
+
+#endif
+
+__clib_export clib_stack_frame_t *
+clib_stack_frame_get (clib_stack_frame_t *sf)
+{
+#if HAVE_LIBUNWIND == 1
+ Dl_info info = {};
+
+ if (sf->index == 0)
+ {
+ if (unw_getcontext (&context) < 0)
+ {
+ clib_warning ("libunwind: cannot get local machine state\n");
+ return 0;
+ }
+ if (unw_init_local (&cursor, &context) < 0)
+ {
+ clib_warning (
+ "libunwind: cannot initialize cursor for local unwinding\n");
+ return 0;
+ }
+ if (unw_step (&cursor) < 1)
+ return 0;
+ }
+ else if (unw_step (&cursor) < 1)
+ return 0;
+
+ if (unw_get_reg (&cursor, UNW_REG_IP, &sf->ip))
+ {
+ clib_warning ("libunwind: cannot read IP\n");
+ return 0;
+ }
+
+ if (unw_get_reg (&cursor, UNW_REG_SP, &sf->sp))
+ {
+ clib_warning ("libunwind: cannot read SP\n");
+ return 0;
+ }
+
+ if (unw_get_proc_name (&cursor, sf->name, sizeof (sf->name), &sf->offset) <
+ 0)
+ sf->name[0] = sf->offset = 0;
+
+ sf->is_signal_frame = unw_is_signal_frame (&cursor) ? 1 : 0;
+
+ if (dladdr ((void *) sf->ip, &info))
+ sf->file_name = info.dli_fname;
+ else
+ sf->file_name = 0;
+
+ sf->index++;
+ return sf;
+#else
+ return 0;
+#endif
+}
diff --git a/src/vppinfra/stack.h b/src/vppinfra/stack.h
new file mode 100644
index 00000000000..98a621d4176
--- /dev/null
+++ b/src/vppinfra/stack.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef __STACK_H__
+#define __STACK_H__
+
+#include <vppinfra/clib.h>
+
+typedef struct
+{
+ uword ip, sp;
+ uword offset;
+ char name[64];
+ const char *file_name;
+ u32 index;
+ u8 is_signal_frame;
+} clib_stack_frame_t;
+
+clib_stack_frame_t *clib_stack_frame_get (clib_stack_frame_t *);
+
+#define foreach_clib_stack_frame(sf) \
+ for (clib_stack_frame_t _sf = {}, *sf = clib_stack_frame_get (&_sf); sf; \
+ sf = clib_stack_frame_get (sf))
+
+#endif /* __STACK_H__ */
diff --git a/src/vppinfra/test_mhash.c b/src/vppinfra/test_mhash.c
new file mode 100644
index 00000000000..70be2b9b382
--- /dev/null
+++ b/src/vppinfra/test_mhash.c
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2023 Yandex LLC.
+ */
+
+#ifdef CLIB_LINUX_KERNEL
+#include <linux/unistd.h>
+#endif
+
+#ifdef CLIB_UNIX
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <vppinfra/time.h>
+#endif
+
+#include <vppinfra/random.h>
+#include <vppinfra/mem.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/mhash.h>
+#include <vppinfra/error.h>
+#include <vppinfra/format.h>
+#include <vppinfra/bitmap.h>
+
+static int verbose;
+#define if_verbose(format, args...) \
+ if (verbose) \
+ { \
+ clib_warning (format, ##args); \
+ }
+
+typedef struct
+{
+ int n_iterations;
+
+ int n_iterations_per_print;
+
+ /* Number of pairs to insert into mhash. */
+ int n_pairs;
+
+ /* True to validate correctness of mhash functions. */
+ int n_iterations_per_validate;
+
+ /* Verbosity level for mhash formats. */
+ int verbose;
+
+ /* Random number seed. */
+ u32 seed;
+} mhash_test_t;
+
+static clib_error_t *
+mhash_next_test (mhash_t *h)
+{
+ hash_next_t hn = { 0 };
+ hash_pair_t *p0, *p1;
+ clib_error_t *error = 0;
+
+ hash_foreach_pair (p0, h->hash, {
+ p1 = hash_next (h->hash, &hn);
+ error = CLIB_ERROR_ASSERT (p0 == p1);
+ if (error)
+ break;
+ });
+
+ if (!error)
+ error = CLIB_ERROR_ASSERT (!hash_next (h->hash, &hn));
+
+ return error;
+}
+
+static clib_error_t *
+test_word_key (mhash_test_t *ht)
+{
+ mhash_t _h = { 0 }, *h = &_h;
+ word i, j;
+
+ word *keys = 0, *vals = 0;
+ uword *is_inserted = 0;
+
+ clib_error_t *error = 0;
+
+ vec_resize (keys, ht->n_pairs);
+ vec_resize (vals, vec_len (keys));
+
+ mhash_init (h, sizeof (vals[0]), sizeof (keys[0]));
+ /* borrow 0 elt to make index keys non-zero */
+ vec_validate (h->key_vector_or_heap, 0);
+
+ {
+ uword *unique = 0;
+ u32 k;
+
+ for (i = 0; i < vec_len (keys); i++)
+ {
+ do
+ {
+ k = random_u32 (&ht->seed) & 0xfffff;
+ }
+ while (clib_bitmap_get (unique, k));
+ unique = clib_bitmap_ori (unique, k);
+ keys[i] = k;
+ vals[i] = i;
+ }
+
+ clib_bitmap_free (unique);
+ }
+
+ for (i = 0; i < ht->n_iterations; i++)
+ {
+ u32 vi = random_u32 (&ht->seed) % vec_len (keys);
+
+ if (clib_bitmap_get (is_inserted, vi))
+ {
+ mhash_unset (h, &keys[vi], 0);
+ mhash_unset (h, &keys[vi], 0);
+ }
+ else
+ {
+ mhash_set (h, &keys[vi], vals[vi], 0);
+ mhash_set (h, &keys[vi], vals[vi], 0);
+ }
+
+ is_inserted = clib_bitmap_xori (is_inserted, vi);
+
+ if (ht->n_iterations_per_print > 0 &&
+ ((i + 1) % ht->n_iterations_per_print) == 0)
+ if_verbose ("iteration %d\n %U", i + 1, format_mhash, h, ht->verbose);
+
+ if (ht->n_iterations_per_validate == 0 ||
+ (i + 1) % ht->n_iterations_per_validate)
+ continue;
+
+ {
+ uword ki, *k, *v;
+
+ mhash_foreach (k, v, h, {
+ ki = v[0];
+ ASSERT (keys[ki] == k[0]);
+ });
+ }
+
+ if ((error = hash_validate (h->hash)))
+ goto done;
+
+ for (j = 0; j < vec_len (keys); j++)
+ {
+ uword *v;
+ v = mhash_get (h, &keys[j]);
+ if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) ==
+ (v != 0))))
+ goto done;
+ if (v)
+ {
+ if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j])))
+ goto done;
+ }
+ }
+ }
+
+ if ((error = mhash_next_test (h)))
+ goto done;
+
+ if_verbose ("%U", format_mhash, h, ht->verbose);
+
+ for (i = 0; i < vec_len (keys); i++)
+ {
+ if (!clib_bitmap_get (is_inserted, i))
+ continue;
+
+ mhash_unset (h, &keys[i], 0);
+ mhash_unset (h, &keys[i], 0);
+ is_inserted = clib_bitmap_xori (is_inserted, i);
+
+ if (ht->n_iterations_per_validate == 0 ||
+ (i + 1) % ht->n_iterations_per_validate)
+ continue;
+
+ if ((error = hash_validate (h->hash)))
+ goto done;
+
+ for (j = 0; j < vec_len (keys); j++)
+ {
+ uword *v;
+ v = mhash_get (h, &keys[j]);
+ if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) ==
+ (v != 0))))
+ goto done;
+ if (v)
+ {
+ if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j])))
+ goto done;
+ }
+ }
+ }
+
+done:
+ mhash_free (h);
+ vec_free (keys);
+ vec_free (vals);
+ clib_bitmap_free (is_inserted);
+
+ if (verbose)
+ fformat (stderr, "%U\n", format_clib_mem_usage, /* verbose */ 0);
+
+ return error;
+}
+
+static u8 *
+test2_format (u8 *s, va_list *args)
+{
+ void *CLIB_UNUSED (user_arg) = va_arg (*args, void *);
+ void *v = va_arg (*args, void *);
+ hash_pair_t *p = va_arg (*args, hash_pair_t *);
+ hash_t *h = hash_header (v);
+ mhash_t *mh = uword_to_pointer (h->user, mhash_t *);
+
+ return format (s, "0x%8U <- %U", format_hex_bytes, &p->value[0],
+ hash_value_bytes (h), format_mhash_key, mh, (u32) p->key);
+}
+
+static clib_error_t *
+test_string_key (mhash_test_t *ht, uword is_c_string)
+{
+ mhash_t _h = { 0 }, *h = &_h;
+ word i, j;
+
+ u8 **keys = 0;
+ word *vals = 0;
+ uword *is_inserted = 0;
+
+ clib_error_t *error = 0;
+
+ vec_resize (keys, ht->n_pairs);
+ vec_resize (vals, vec_len (keys));
+
+ if (is_c_string)
+ mhash_init_c_string (h, sizeof (vals[0]));
+ else
+ mhash_init_vec_string (h, sizeof (vals[0]));
+ hash_set_pair_format (h->hash, test2_format, 0);
+
+ for (i = 0; i < vec_len (keys); i++)
+ {
+ keys[i] = random_string (&ht->seed, 5 + (random_u32 (&ht->seed) & 0xf));
+ keys[i] = format (keys[i], "%x", i);
+ if (is_c_string)
+ vec_terminate_c_string (keys[i]);
+ vals[i] = random_u32 (&ht->seed);
+ }
+
+ for (i = 0; i < ht->n_iterations; i++)
+ {
+ u32 vi = random_u32 (&ht->seed) % vec_len (keys);
+
+ if (clib_bitmap_get (is_inserted, vi))
+ {
+ mhash_unset (h, keys[vi], 0);
+ mhash_unset (h, keys[vi], 0);
+ }
+ else
+ {
+ mhash_set (h, keys[vi], vals[vi], 0);
+ mhash_set (h, keys[vi], vals[vi], 0);
+ }
+
+ is_inserted = clib_bitmap_xori (is_inserted, vi);
+
+ if (ht->n_iterations_per_print > 0 &&
+ ((i + 1) % ht->n_iterations_per_print) == 0)
+ if_verbose ("iteration %d\n %U", i + 1, format_mhash, h, ht->verbose);
+
+ if (ht->n_iterations_per_validate == 0 ||
+ (i + 1) % ht->n_iterations_per_validate)
+ continue;
+
+ if ((error = hash_validate (h->hash)))
+ goto done;
+
+ for (j = 0; j < vec_len (keys); j++)
+ {
+ uword *v;
+ v = mhash_get (h, keys[j]);
+ if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) ==
+ (v != 0))))
+ goto done;
+ if (v)
+ {
+ if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j])))
+ goto done;
+ }
+ }
+ }
+
+ if ((error = mhash_next_test (h)))
+ goto done;
+
+ if_verbose ("%U", format_mhash, h, ht->verbose);
+
+ for (i = 0; i < vec_len (keys); i++)
+ {
+ if (!clib_bitmap_get (is_inserted, i))
+ continue;
+
+ mhash_unset (h, keys[i], 0);
+ mhash_unset (h, keys[i], 0);
+ is_inserted = clib_bitmap_xori (is_inserted, i);
+
+ if (ht->n_iterations_per_validate == 0 ||
+ (i + 1) % ht->n_iterations_per_validate)
+ continue;
+
+ if ((error = hash_validate (h->hash)))
+ goto done;
+
+ for (j = 0; j < vec_len (keys); j++)
+ {
+ uword *v;
+ v = mhash_get (h, keys[j]);
+ if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) ==
+ (v != 0))))
+ goto done;
+ if (v)
+ {
+ if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j])))
+ goto done;
+ }
+ }
+ }
+
+done:
+ mhash_free (h);
+ vec_free (vals);
+ clib_bitmap_free (is_inserted);
+
+ for (i = 0; i < vec_len (keys); i++)
+ vec_free (keys[i]);
+ vec_free (keys);
+
+ if (verbose)
+ fformat (stderr, "%U\n", format_clib_mem_usage, /* verbose */ 0);
+
+ return error;
+}
+
+int
+test_mhash_main (unformat_input_t *input)
+{
+ mhash_test_t _ht = { 0 }, *ht = &_ht;
+ clib_error_t *error;
+
+ ht->n_iterations = 100;
+ ht->n_pairs = 10;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (0 == unformat (input, "iter %d", &ht->n_iterations) &&
+ 0 == unformat (input, "print %d", &ht->n_iterations_per_print) &&
+ 0 == unformat (input, "elts %d", &ht->n_pairs) &&
+ 0 == unformat (input, "seed %d", &ht->seed) &&
+ 0 == unformat (input, "verbose %=", &ht->verbose, 1) &&
+ 0 == unformat (input, "valid %d", &ht->n_iterations_per_validate))
+ {
+ clib_warning ("unknown input `%U'", format_unformat_error, input);
+ return 1;
+ }
+ }
+
+ if (!ht->seed)
+ ht->seed = random_default_seed ();
+
+ if_verbose ("testing %d iterations, seed %d", ht->n_iterations, ht->seed);
+
+ error = test_word_key (ht);
+ if (error)
+ clib_error_report (error);
+
+ error = test_string_key (ht, 0);
+ if (error)
+ clib_error_report (error);
+
+ error = test_string_key (ht, 1);
+ if (error)
+ clib_error_report (error);
+
+ return 0;
+}
+
+#ifdef CLIB_UNIX
+int
+main (int argc, char *argv[])
+{
+ unformat_input_t i;
+ int ret;
+
+ clib_mem_init (0, 3ULL << 30);
+
+ verbose = (argc > 1);
+ unformat_init_command_line (&i, argv);
+ ret = test_mhash_main (&i);
+ unformat_free (&i);
+
+ return ret;
+}
+#endif /* CLIB_UNIX */
diff --git a/src/vppinfra/time.c b/src/vppinfra/time.c
index 5a6aaf182e4..f1736499a0a 100644
--- a/src/vppinfra/time.c
+++ b/src/vppinfra/time.c
@@ -76,8 +76,11 @@ clock_frequency_from_proc_filesystem (void)
f64 ppc_timebase = 0; /* warnings be gone */
unformat_input_t input;
-/* $$$$ aarch64 kernel doesn't report "cpu MHz" */
-#if defined(__aarch64__)
+#if defined(__x86_64__)
+ if (clib_cpu_supports_aperfmperf ())
+ return 0.0;
+#elif defined(__aarch64__)
+ /* $$$$ aarch64 kernel doesn't report "cpu MHz" */
return 0.0;
#endif
diff --git a/src/vppinfra/time_range.c b/src/vppinfra/time_range.c
index 4b5e1303763..54f5629641a 100644
--- a/src/vppinfra/time_range.c
+++ b/src/vppinfra/time_range.c
@@ -264,11 +264,10 @@ format_clib_timebase_time (u8 * s, va_list * args)
clib_timebase_time_to_components (now, cp);
- s = format (s, "%s, %u %s %u %u:%02u:%02u",
- day_names_epoch_order[cp->day_name_index],
- cp->day,
- month_short_names[cp->month],
- cp->year, cp->hour, cp->minute, cp->second);
+ s = format (s, "%s, %02u %s %u %02u:%02u:%02u",
+ day_names_epoch_order[cp->day_name_index], cp->day,
+ month_short_names[cp->month], cp->year, cp->hour, cp->minute,
+ cp->second);
return (s);
}
diff --git a/src/vppinfra/unix-misc.c b/src/vppinfra/unix-misc.c
index 5008f82c493..05ca2f901c6 100644
--- a/src/vppinfra/unix-misc.c
+++ b/src/vppinfra/unix-misc.c
@@ -35,6 +35,10 @@
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#include <vppinfra/error.h>
#include <vppinfra/os.h>
#include <vppinfra/bitmap.h>
@@ -42,9 +46,15 @@
#include <vppinfra/format.h>
#ifdef __linux__
#include <vppinfra/linux/sysfs.h>
+#include <sched.h>
#elif defined(__FreeBSD__)
-#include <sys/sysctl.h>
+#define _WANT_FREEBSD_BITSET
+#include <sys/cdefs.h>
#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/cpuset.h>
+#include <sys/domainset.h>
+#include <sys/sysctl.h>
#endif
#include <sys/stat.h>
@@ -57,6 +67,8 @@
__clib_export __thread uword __os_thread_index = 0;
__clib_export __thread uword __os_numa_index = 0;
+__clib_export clib_bitmap_t *os_get_cpu_affinity_bitmap (int pid);
+
clib_error_t *
clib_file_n_bytes (char *file, uword * result)
{
@@ -209,27 +221,20 @@ unix_proc_file_contents (char *file, u8 ** result)
return 0;
}
-void os_panic (void) __attribute__ ((weak));
-
-__clib_export void
+__clib_export __clib_weak void
os_panic (void)
{
abort ();
}
-void os_exit (int) __attribute__ ((weak));
-
-void
+__clib_export __clib_weak void
os_exit (int code)
{
exit (code);
}
-void os_puts (u8 * string, uword string_length, uword is_error)
- __attribute__ ((weak));
-
-void
-os_puts (u8 * string, uword string_length, uword is_error)
+__clib_export __clib_weak void
+os_puts (u8 *string, uword string_length, uword is_error)
{
int cpu = os_get_thread_index ();
int nthreads = os_get_nthreads ();
@@ -272,12 +277,62 @@ os_get_online_cpu_core_bitmap ()
{
#if __linux__
return clib_sysfs_read_bitmap ("/sys/devices/system/cpu/online");
+#elif defined(__FreeBSD__)
+ return os_get_cpu_affinity_bitmap (0);
#else
return 0;
#endif
}
__clib_export clib_bitmap_t *
+os_get_cpu_affinity_bitmap (int pid)
+{
+#if __linux
+ int index, ret;
+ cpu_set_t cpuset;
+ uword *affinity_cpus;
+
+ clib_bitmap_alloc (affinity_cpus, sizeof (cpu_set_t));
+ clib_bitmap_zero (affinity_cpus);
+
+ CPU_ZERO_S (sizeof (cpu_set_t), &cpuset);
+
+ ret = sched_getaffinity (0, sizeof (cpu_set_t), &cpuset);
+
+ if (ret < 0)
+ {
+ clib_bitmap_free (affinity_cpus);
+ return 0;
+ }
+
+ for (index = 0; index < sizeof (cpu_set_t); index++)
+ if (CPU_ISSET_S (index, sizeof (cpu_set_t), &cpuset))
+ clib_bitmap_set (affinity_cpus, index, 1);
+ return affinity_cpus;
+#elif defined(__FreeBSD__)
+ cpuset_t mask;
+ uword *r = NULL;
+
+ clib_bitmap_alloc (r, sizeof (CPU_SETSIZE));
+ clib_bitmap_zero (r);
+
+ if (cpuset_getaffinity (CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, -1,
+ sizeof (mask), &mask) != 0)
+ {
+ clib_bitmap_free (r);
+ return NULL;
+ }
+
+ for (int bit = 0; bit < CPU_SETSIZE; bit++)
+ clib_bitmap_set (r, bit, CPU_ISSET (bit, &mask));
+
+ return r;
+#else
+ return NULL;
+#endif
+}
+
+__clib_export clib_bitmap_t *
os_get_online_cpu_node_bitmap ()
{
#if __linux__
diff --git a/test/Makefile b/test/Makefile
index 9b9cc178cf6..bc5bc0cf129 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -74,12 +74,13 @@ V=0
endif
PYTHON_VERSION=$(shell $(PYTHON_INTERP) -c 'import sys; print(sys.version_info.major)')
-PIP_VERSION=24.0
+PIP_VERSION=24.2
# Keep in sync with requirements.txt
PIP_TOOLS_VERSION=7.4.1
-PIP_SETUPTOOLS_VERSION=69.2.0
+PIP_SETUPTOOLS_VERSION=71.1.0
PYTHON_DEPENDS=requirements-$(PYTHON_VERSION).txt
SCAPY_SOURCE=$(shell find $(VENV_PATH)/lib/python* -name site-packages)
+SCAPY_VERSION=$(shell grep scapy $(TEST_DIR)/requirements.txt | cut -d'=' -f3 | cut -d';' -f1)
BUILD_COV_DIR=$(BR)/test-coverage
PIP_TOOLS_INSTALL_DONE=$(VENV_RUN_DIR)/pip-tools-install-$(PYTHON_VERSION)-$(PIP_TOOLS_VERSION).done
@@ -118,7 +119,7 @@ $(PIP_INSTALL_DONE): $(PIP_TOOLS_INSTALL_DONE) $(PYTHON_DEPENDS)
$(PIP_PATCH_DONE): $(PIP_INSTALL_DONE)
@echo --- patching ---
@sleep 1 # Ensure python recompiles patched *.py files -> *.pyc
- for f in $(CURDIR)/patches/scapy-2.4.3/*.patch ; do \
+ for f in $(CURDIR)/patches/scapy-$(SCAPY_VERSION)/*.patch ; do \
echo Applying patch: $$(basename $$f) ; \
patch --forward -p1 -d $(SCAPY_SOURCE) < $$f ; \
retCode=$$?; \
@@ -255,8 +256,8 @@ ARG17=--extern-apidir=$(EXTERN_APIDIR)
endif
ARG18=
-ifneq ($(findstring $(DECODE_PCAPS),1 y yes),)
-ARG18=--decode-pcaps
+ifneq ($(DECODE_PCAPS),)
+ARG18=--decode-pcaps=$(DECODE_PCAPS)
endif
ifneq ($(findstring $(API_PRELOAD),1 y yes),)
@@ -350,32 +351,72 @@ cov-prep: test-dep
@lcov --zerocounters --directory $(VPP_BUILD_DIR)
@test -z "$(EXTERN_COV_DIR)" || lcov --zerocounters --directory $(EXTERN_COV_DIR)
+COV_REM_NOT_CODE="/usr/include/*" "*/build-root/*" "/opt/*" "/usr/lib/*" \
+ "*_test.*" "*test_*" "*vat*" "*/vnet/unix/gdb_funcs.c" \
+ "*pg.c"
+
+COV_REM_DRIVERS="*rdma*" "*/plugins/af_packet/*" "*/plugins/af_xdp/*" \
+ "*/plugins/avf/*" "*/plugins/dma_intel/*" "*/vlib/pci/*" \
+ "*/vnet/devices/*" "*/vlib/dma/*" "*/plugins/vmxnet3/*" \
+ "*/vnet/devices/virtio/*" "*/plugins/perfmon/arm*" \
+ "*/plugins/perfmon/intel/*" "*/vlib/vmbus/*" \
+ "*/vnet/dev/*" "*/plugins/dev_ena/*" "*/plugins/dev_iavf/*"
+
+COV_REM_UNUSED_FEAT="*/plugins/ioam/analyse/*" "*/plugins/ioam/lib-*/*" \
+ "*/plugins/ioam/export-common/*" "*/vnet/srp/*" \
+ "*/lawful-intercept/*" "*/lisp/*" "*/vnet/osi/*" \
+ "*/plugins/nsh/*"
+
+COV_REM_TODO_NO_TEST="*/vpp-api/client/*" "*/plugins/prom/*" \
+ "*/plugins/tlspicotls/*" "*/plugins/tlsmbedtls/*" \
+ "*/vppinfra/perfmon/*" "*/plugins/ila/*" \
+ "*/vlib/linux/*" "*/vnet/util/radix.c" "*/vapi/vapi.hpp" \
+ "*/vpp/api/types.c" "*/vpp/api/json_format.c" \
+ "*/plugins/ioam/*/*.h" "*/linux/netns.c" "*/vnet/flow/*" \
+ "*/vppinfra/random.c" "*/vppinfra/ring.h" \
+ "*/vppinfra/bihash_vec8_8.h" "*/vppinfra/maplog.c" \
+ "*/vppinfra/format_table.c" "*/vppinfra/timing_wheel.c" \
+ "*/vppinfra/macros.c" "*/vppinfra/valloc.c" \
+ "*/vppinfra/jsonformat.c" "*/vppinfra/vector/array_mask.h" \
+ "*/vppinfra/vector/toeplitz.c" "*/plugins/vrrp/vrrp_packet.h" \
+ "*/vnet/srv6/sr.h" "*/vlibapi/api_format.c" \
+ "*/vlibapi/node_serialize.c" "*/plugins/quic/error.c" \
+ "*/vnet/ipfix-export/flow_report_classify.h" \
+ "*/vnet/ip/ip6_ll_types.c" "*/vnet/ip/ip_psh_cksum.h" \
+ "*/vnet/ip/ip6_hop_by_hop.h" "*/vnet/ip/ip_format_fns.h" \
+ "*/vnet/dpo/classify_dpo.h" "*/vnet/dpo/l3_proxy_dpo.h" \
+ "*/vnet/ipsec/esp_format.c" "*/vnet/ethernet/sfp.c" \
+ "*/vnet/ethernet/ethernet_format_fns.h" \
+ "*/plugins/ikev2/ikev2_format.c" "*/vnet/bier/bier_types.c"
+
+COV_REM_ALT_TEST="*/plugins/hs_apps/*" "*/plugins/http/*.h"
+
.PHONY: cov-post
cov-post: wipe-cov $(BUILD_COV_DIR)
@lcov --capture \
--directory $(VPP_BUILD_DIR) \
- --output-file $(BUILD_COV_DIR)/coverage.info
+ --output-file $(BUILD_COV_DIR)/coverage$(HS_TEST).info
@test -z "$(EXTERN_COV_DIR)" || \
lcov --capture \
--directory $(EXTERN_COV_DIR) \
- --output-file $(BUILD_COV_DIR)/extern-coverage.info
- @lcov --remove $(BUILD_COV_DIR)/coverage.info \
- "/usr/include/*" "*/build-root/*" "/opt/*" "/usr/lib/*" \
- "*_test.*" "*vat*" "*rdma*" "*/vpp-api/client/*" "*/plugins/af_packet/*" \
- "*/plugins/af_xdp/*" "*/plugins/avf/*" "*/plugins/dma_intel/*" \
- "*/plugins/hs_apps/*" "*/plugins/vmxnet3/*" "*/vnet/devices/virtio/*" \
- "*/plugins/perfmon/arm*" "*/plugins/perfmon/intel/*" "*/vlib/vmbus/*" \
- "*/vnet/dev/*" "*/plugins/dev_ena/*" "*/plugins/builtinurl/*" "*/vnet/flow/*" \
- "*/plugins/http_static/builtinurl/*" "*/plugins/dev_iavf/*" \
- -o $(BUILD_COV_DIR)/coverage-filtered.info
- @genhtml $(BUILD_COV_DIR)/coverage-filtered.info \
+ --output-file $(BUILD_COV_DIR)/extern-coverage$(HS_TEST).info
+ @lcov --remove $(BUILD_COV_DIR)/coverage$(HS_TEST).info \
+ $(COV_REM_NOT_CODE) \
+ $(COV_REM_DRIVERS) \
+ $(COV_REM_TODO_NO_TEST) \
+ $(COV_REM_UNUSED_FEAT) \
+ $(COV_REM_ALT_TEST) \
+ -o $(BUILD_COV_DIR)/coverage-filtered$(HS_TEST).info
+ @genhtml $(BUILD_COV_DIR)/coverage-filtered$(HS_TEST).info \
--output-directory $(BUILD_COV_DIR)/html
@test -z "$(EXTERN_COV_DIR)" || \
- genhtml $(BUILD_COV_DIR)/extern-coverage.info \
+ genhtml $(BUILD_COV_DIR)/extern-coverage$(HS_TEST).info \
--output-directory $(BUILD_COV_DIR)/extern-html
@echo
@echo "Build finished. Code coverage report is in $(BUILD_COV_DIR)/html/index.html"
@test -z "$(EXTERN_COV_DIR)" || echo "Code coverage report for out-of-tree objects is in $(BUILD_COV_DIR)/extern-html/index.html"
+ @mkdir -p $(BR)/test-coverage-merged
+ @cp -f $(BUILD_COV_DIR)/coverage-filtered$(HS_TEST).info $(BR)/test-coverage-merged
.PHONY: cov
cov:
@@ -434,6 +475,7 @@ help:
@echo " test-cov-prep - coverage phase #1 : prepare lcov"
@echo " test-cov-build - coverage phase #2 : build gcov image & run tests against it (use TEST=)"
@echo " test-cov-post - coverage phase #3 : generate lcov html report"
+ @echo " test-cov-both - generate and merge code coverage report for Python and Golang tests"
@echo " test-all - build and run functional and extended tests"
@echo " test-all-debug - build and run functional and extended tests (debug build)"
@echo " test-all-cov - generate code coverage report for functional and extended tests"
@@ -613,6 +655,10 @@ help:
@echo " random seed used by test framework"
@echo " (default: time.time())"
@echo ""
+ @echo " DECODE_PCAPS=[all|failed|none]"
+ @echo " decode pcap files using tshark - all, only failed or none"
+ @echo " (default: failed)"
+ @echo ""
@echo "Starting VPP in GDB for use with DEBUG=attach:"
@echo ""
@echo " test-start-vpp-in-gdb - start VPP in gdb (release)"
diff --git a/test/asf/asfframework.py b/test/asf/asfframework.py
index 24880044cec..bd1b45c6476 100644
--- a/test/asf/asfframework.py
+++ b/test/asf/asfframework.py
@@ -166,25 +166,28 @@ def _is_distro_ubuntu2204():
is_distro_ubuntu2204 = _is_distro_ubuntu2204()
-def _is_distro_debian11():
+def _is_distro_ubuntu2404():
with open("/etc/os-release") as f:
for line in f.readlines():
- if "bullseye" in line:
+ if "noble" in line:
return True
return False
-is_distro_debian11 = _is_distro_debian11()
+is_distro_ubuntu2404 = _is_distro_ubuntu2404()
-def _is_distro_ubuntu2204():
+def _is_distro_debian11():
with open("/etc/os-release") as f:
for line in f.readlines():
- if "jammy" in line:
+ if "bullseye" in line:
return True
return False
+is_distro_debian11 = _is_distro_debian11()
+
+
class KeepAliveReporter(object):
"""
Singleton object which reports test start to parent process
@@ -236,6 +239,8 @@ class TestCaseTag(Enum):
FIXME_DEBIAN11 = 5
# marks suites broken on debug vpp image
FIXME_VPP_DEBUG = 6
+ # marks suites broken on Ubuntu-24.04
+ FIXME_UBUNTU2404 = 7
def create_tag_decorator(e):
@@ -255,6 +260,7 @@ tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
tag_fixme_ubuntu2204 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2204)
tag_fixme_debian11 = create_tag_decorator(TestCaseTag.FIXME_DEBIAN11)
tag_fixme_vpp_debug = create_tag_decorator(TestCaseTag.FIXME_VPP_DEBUG)
+tag_fixme_ubuntu2404 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2404)
class DummyVpp:
@@ -317,6 +323,18 @@ class VppAsfTestCase(CPUInterface, unittest.TestCase):
cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
@classmethod
+ def skip_fixme_ubuntu2204(cls):
+ """if @tag_fixme_ubuntu2204 & is Ubuntu22.04 - mark for skip"""
+ if cls.has_tag(TestCaseTag.FIXME_UBUNTU2204) and is_distro_ubuntu2204 == True:
+ cls = unittest.skip("Skipping @tag_fixme_ubuntu2204 tests")(cls)
+
+ @classmethod
+ def skip_fixme_ubuntu2404(cls):
+ """if @tag_fixme_ubuntu2404 & is Ubuntu24.04 - mark for skip"""
+ if cls.has_tag(TestCaseTag.FIXME_UBUNTU2404) and is_distro_ubuntu2404 == True:
+ cls = unittest.skip("Skipping @tag_fixme_ubuntu2404 tests")(cls)
+
+ @classmethod
def instance(cls):
"""Return the instance of this testcase"""
return cls.test_instance
@@ -1155,16 +1173,13 @@ class VppTestResult(unittest.TestResult):
self.runner = runner
self.printed = []
- def decodePcapFiles(self, test, when_configured=False):
- if when_configured == False or config.decode_pcaps == True:
- if hasattr(test, "pg_interfaces") and len(test.pg_interfaces) > 0:
- testcase_dir = os.path.dirname(test.pg_interfaces[0].out_path)
- test.pg_interfaces[0].decode_pcap_files(
- testcase_dir, f"suite{test.__class__.__name__}"
- )
- test.pg_interfaces[0].decode_pcap_files(
- testcase_dir, test._testMethodName
- )
+ def decodePcapFiles(self, test):
+ if hasattr(test, "pg_interfaces") and len(test.pg_interfaces) > 0:
+ testcase_dir = os.path.dirname(test.pg_interfaces[0].out_path)
+ test.pg_interfaces[0].decode_pcap_files(
+ testcase_dir, f"suite{test.__class__.__name__}"
+ )
+ test.pg_interfaces[0].decode_pcap_files(testcase_dir, test._testMethodName)
def addSuccess(self, test):
"""
@@ -1174,7 +1189,8 @@ class VppTestResult(unittest.TestResult):
"""
self.log_result("addSuccess", test)
- self.decodePcapFiles(test, when_configured=True)
+ if "all" == config.decode_pcaps:
+ self.decodePcapFiles(test)
unittest.TestResult.addSuccess(self, test)
self.result_string = colorize("OK", GREEN)
self.result_code = TestResultCode.PASS
@@ -1182,7 +1198,8 @@ class VppTestResult(unittest.TestResult):
def addExpectedFailure(self, test, err):
self.log_result("addExpectedFailure", test, err)
- self.decodePcapFiles(test)
+ if "none" != config.decode_pcaps:
+ self.decodePcapFiles(test)
super().addExpectedFailure(test, err)
self.result_string = colorize("FAIL", GREEN)
self.result_code = TestResultCode.EXPECTED_FAIL
@@ -1190,7 +1207,8 @@ class VppTestResult(unittest.TestResult):
def addUnexpectedSuccess(self, test):
self.log_result("addUnexpectedSuccess", test)
- self.decodePcapFiles(test, when_configured=True)
+ if "none" != config.decode_pcaps:
+ self.decodePcapFiles(test)
super().addUnexpectedSuccess(test)
self.result_string = colorize("OK", RED)
self.result_code = TestResultCode.UNEXPECTED_PASS
@@ -1276,7 +1294,9 @@ class VppTestResult(unittest.TestResult):
error_type_str = colorize("ERROR", RED)
else:
raise Exception(f"Unexpected result code {result_code}")
- self.decodePcapFiles(test)
+
+ if "none" != config.decode_pcaps:
+ self.decodePcapFiles(test)
unittest_fn(self, test, err)
if self.current_test_case_info:
@@ -1361,6 +1381,20 @@ class VppTestResult(unittest.TestResult):
test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
test.skip_fixme_asan()
+ if (
+ test.has_tag(TestCaseTag.FIXME_UBUNTU2204)
+ and is_distro_ubuntu2204 == True
+ ):
+ test_title = colorize(f"FIXME with Ubuntu 22.04: {test_title}", RED)
+ test.skip_fixme_ubuntu2204()
+
+ if (
+ test.has_tag(TestCaseTag.FIXME_UBUNTU2404)
+ and is_distro_ubuntu2404 == True
+ ):
+ test_title = colorize(f"FIXME with Ubuntu 24.04: {test_title}", RED)
+ test.skip_fixme_ubuntu2404()
+
if hasattr(test, "vpp_worker_count"):
if test.vpp_worker_count == 0:
test_title += " [main thread only]"
diff --git a/test/asf/test_adl.py b/test/asf/test_adl.py
index 7e5ca8dcbe3..70d32bce9da 100644
--- a/test/asf/test_adl.py
+++ b/test/asf/test_adl.py
@@ -3,8 +3,10 @@
import unittest
from asfframework import VppAsfTestCase, VppTestRunner
+from config import config
+@unittest.skipIf("adl" in config.excluded_plugins, "Exclude ADL plugin tests")
class TestAdl(VppAsfTestCase):
"""Allow/Deny Plugin Unit Test Cases"""
diff --git a/test/asf/test_api_trace.py b/test/asf/test_api_trace.py
index 8776a79f0ac..04fcf63ca41 100644
--- a/test/asf/test_api_trace.py
+++ b/test/asf/test_api_trace.py
@@ -37,7 +37,7 @@ class TestJsonApiTrace(VppAsfTestCase):
found = True
break
self.assertTrue(found)
- self.assertEquals(o["_msgname"], "show_version")
+ self.assertEqual(o["_msgname"], "show_version")
def test_json_api_trace_replay(self):
fname = "/tmp/create_loop.json"
diff --git a/test/asf/test_http_static.py b/test/asf/test_http_static.py
index 18e8ba56a1e..73a95e992da 100644
--- a/test/asf/test_http_static.py
+++ b/test/asf/test_http_static.py
@@ -63,11 +63,13 @@ class TestHttpStaticVapi(VppAsfTestCase):
"exec",
"HttpStatic",
"curl",
+ "-v",
f"10.10.1.2/{self.temp.name[5:]}",
],
capture_output=True,
)
self.assertIn(b"Hello world", process.stdout)
+ self.assertIn(b"max-age=600", process.stderr)
self.temp2.seek(0)
process = subprocess.run(
diff --git a/test/asf/test_lb_api.py b/test/asf/test_lb_api.py
index 9608d0473a6..031479eb7f4 100644
--- a/test/asf/test_lb_api.py
+++ b/test/asf/test_lb_api.py
@@ -13,10 +13,14 @@
# limitations under the License.
from asfframework import VppAsfTestCase
+from config import config
+
+import unittest
DEFAULT_VIP = "lb_vip_details(_0=978, context=12, vip=vl_api_lb_ip_addr_t(pfx=IPv6Network(u'::/0'), protocol=<vl_api_ip_proto_t.IP_API_PROTO_RESERVED: 255>, port=0), encap=<vl_api_lb_encap_type_t.LB_API_ENCAP_TYPE_GRE4: 0>, dscp=<vl_api_ip_dscp_t.IP_API_DSCP_CS0: 0>, srv_type=<vl_api_lb_srv_type_t.LB_API_SRV_TYPE_CLUSTERIP: 0>, target_port=0, flow_table_length=0)" # noqa
+@unittest.skipIf("lb" in config.excluded_plugins, "Exclude LB plugin tests")
class TestLbEmptyApi(VppAsfTestCase):
"""TestLbEmptyApi"""
@@ -34,6 +38,7 @@ class TestLbEmptyApi(VppAsfTestCase):
self.assertEqual(rv, [], "Expected: [] Received: %r." % rv)
+@unittest.skipIf("lb" in config.excluded_plugins, "Exclude LB plugin tests")
class TestLbApi(VppAsfTestCase):
"""TestLbApi"""
@@ -55,6 +60,7 @@ class TestLbApi(VppAsfTestCase):
self.vapi.cli("lb vip 2001::/16 del")
+@unittest.skipIf("lb" in config.excluded_plugins, "Exclude LB plugin tests")
class TestLbAsApi(VppAsfTestCase):
"""TestLbAsApi"""
diff --git a/test/asf/test_prom.py b/test/asf/test_prom.py
index 3f8fb4c7a44..f536fd19d34 100644
--- a/test/asf/test_prom.py
+++ b/test/asf/test_prom.py
@@ -39,6 +39,7 @@ class TestProm(VppAsfTestCase):
"""Enable HTTP Static server and prometheus exporter, get stats"""
self.vapi.cli("http static server uri tcp://0.0.0.0/80 url-handlers")
self.vapi.cli("prom enable")
+ self.sleep(1, "wait for min-scrape-interval to expire")
process = subprocess.run(
[
diff --git a/test/asf/test_quic.py b/test/asf/test_quic.py
index e453bd5b3e5..c4fa6912114 100644
--- a/test/asf/test_quic.py
+++ b/test/asf/test_quic.py
@@ -117,6 +117,18 @@ class QUICTestCase(VppAsfTestCase):
self.logger.debug(self.vapi.cli("show ip fib"))
def tearDown(self):
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0,
+ namespace_id=self.server_appns,
+ secret=self.server_appns_secret,
+ sw_if_index=self.loop0.sw_if_index,
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0,
+ namespace_id=self.client_appns,
+ secret=self.client_appns_secret,
+ sw_if_index=self.loop1.sw_if_index,
+ )
# Delete inter-table routes
self.ip_t01.remove_vpp_config()
self.ip_t10.remove_vpp_config()
diff --git a/test/asf/test_session.py b/test/asf/test_session.py
index 64f59df5758..184ec4fba54 100644
--- a/test/asf/test_session.py
+++ b/test/asf/test_session.py
@@ -9,9 +9,13 @@ from asfframework import (
tag_run_solo,
)
from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
+from config import config
@tag_fixme_vpp_workers
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class TestSession(VppAsfTestCase):
"""Session Test Case"""
@@ -56,6 +60,14 @@ class TestSession(VppAsfTestCase):
i.set_table_ip4(0)
i.admin_down()
+ # Unconfigure namespaces - remove our locks to the vrf tables
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
+
super(TestSession, self).tearDown()
self.vapi.session_enable_disable(is_enable=1)
diff --git a/test/asf/test_session_sdl.py b/test/asf/test_session_sdl.py
new file mode 100644
index 00000000000..15d696350bc
--- /dev/null
+++ b/test/asf/test_session_sdl.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+
+import unittest
+
+from framework import VppTestCase
+from asfframework import VppTestRunner, tag_fixme_vpp_workers
+from ipaddress import IPv4Network, IPv6Network
+
+from vpp_ip_route import (
+ VppIpRoute,
+ VppRoutePath,
+ VppIpTable,
+)
+
+from vpp_papi import VppEnum
+
+
+from vpp_session_sdl import VppSessionSdl
+from vpp_session_sdl import SessionSdl
+
+
+@tag_fixme_vpp_workers
+class TestSessionSDL(VppTestCase):
+ """Session SDL Test Case"""
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSessionSDL, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestSessionSDL, cls).tearDownClass()
+
+ def setUp(self):
+ super(TestSessionSDL, self).setUp()
+ self.create_loopback_interfaces(2)
+
+ table_id = 0
+
+ for i in self.lo_interfaces:
+ i.admin_up()
+
+ if table_id != 0:
+ tbl = VppIpTable(self, table_id)
+ tbl.add_vpp_config()
+ tbl = VppIpTable(self, table_id, is_ip6=1)
+ tbl.add_vpp_config()
+
+ i.set_table_ip4(table_id)
+ i.set_table_ip6(table_id)
+ i.config_ip4()
+ i.config_ip6()
+ table_id += 1
+
+ def tearDown(self):
+ for i in self.lo_interfaces:
+ i.unconfig_ip4()
+ i.set_table_ip4(0)
+ i.unconfig_ip6()
+ i.set_table_ip6(0)
+ i.admin_down()
+ self.loop0.remove_vpp_config()
+ self.loop1.remove_vpp_config()
+ super(TestSessionSDL, self).tearDown()
+
+ def create_rule(self, lcl, action_index, tag):
+ return SessionSdl(lcl=lcl, action_index=action_index, tag=tag)
+
+ def apply_rules(self, rules, is_add, appns_index):
+ r = VppSessionSdl(self, rules, is_add=is_add, appns_index=appns_index)
+ r.add_vpp_config()
+
+ def test_session_sdl_ip4(self):
+ """Session SDL IP4 test"""
+
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_SDL
+ )
+
+ # Configure namespaces
+ self.vapi.app_namespace_add_del_v4(
+ namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
+
+ # Add inter-table routes
+ uri = "tcp://" + self.loop0.local_ip4 + "/1234"
+ server_cmd = "test echo server appns 0 fifo-size 4k " + "uri " + uri
+ client_cmd = (
+ "test echo client bytes 100000 appns 1 "
+ + "fifo-size 4k "
+ + "syn-timeout 2 uri "
+ + uri
+ )
+ ip_t01 = VppIpRoute(
+ self,
+ self.loop1.local_ip4,
+ 32,
+ [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)],
+ )
+ ip_t10 = VppIpRoute(
+ self,
+ self.loop0.local_ip4,
+ 32,
+ [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)],
+ table_id=1,
+ )
+ ip_t01.add_vpp_config()
+ ip_t10.add_vpp_config()
+
+ # Start builtin server for ip4
+ self.logger.info(self.vapi.cli(server_cmd))
+
+ # Add session filter to block loop0
+ rules = []
+ rules.append(
+ self.create_rule(lcl=self.loop0.local_ip4 + "/32", action_index=0, tag="")
+ )
+ self.apply_rules(rules, is_add=1, appns_index=0)
+
+ filter = self.vapi.session_sdl_dump()
+ self.assertEqual(filter[0].lcl, IPv4Network(self.loop0.local_ip4 + "/32"))
+
+ # irrelevant rules - add 64k entries in one API call
+ rules = []
+ for i in range(255):
+ for j in range(255):
+ prefix = "10.1.{0}.{1}/32".format(i, j)
+ rules.append(self.create_rule(lcl=prefix, action_index=0, tag=""))
+ self.apply_rules(rules, is_add=1, appns_index=0)
+
+ error = self.vapi.cli_return_response(server_cmd)
+ # Expecting an error because loop0 is blocked
+ self.assertEqual(-1, error.retval)
+
+ # Remove the session filter
+ rules = []
+ rules.append(
+ self.create_rule(lcl=self.loop0.local_ip4 + "/32", action_index=0, tag="")
+ )
+ self.apply_rules(rules, is_add=0, appns_index=0)
+
+ # Not expecting an error
+ self.logger.info(self.vapi.cli(client_cmd))
+
+ # Add a session filter not matching loop0
+ rules = []
+ rules.append(self.create_rule(lcl="172.100.1.0/24", action_index=0, tag=""))
+ self.apply_rules(rules, is_add=1, appns_index=0)
+
+ # Not expecting an error
+ self.logger.info(self.vapi.cli(client_cmd))
+
+ self.logger.info(self.vapi.cli(server_cmd + " stop"))
+
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
+ # Delete inter-table routes
+ ip_t01.remove_vpp_config()
+ ip_t10.remove_vpp_config()
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+ )
+
+ def test_session_sdl_ip6(self):
+ """Session SDL IP6 test"""
+
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_SDL
+ )
+
+ # Configure namespaces
+ self.vapi.app_namespace_add_del_v4(
+ namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
+
+ # IP6 Test
+ # Add inter-table routes
+ uri = "tcp://" + self.loop0.local_ip6 + "/1235"
+ client_cmd = (
+ "test echo client bytes 100000 appns 1 "
+ + "fifo-size 4k "
+ + "syn-timeout 2 uri "
+ + uri
+ )
+ server_cmd = "test echo server appns 0 fifo-size 4k " + "uri " + uri
+
+ ip_t01 = VppIpRoute(
+ self,
+ self.loop1.local_ip6,
+ 128,
+ [VppRoutePath("0::0", 0xFFFFFFFF, nh_table_id=1)],
+ )
+ ip_t10 = VppIpRoute(
+ self,
+ self.loop0.local_ip6,
+ 128,
+ [VppRoutePath("0::0", 0xFFFFFFFF, nh_table_id=0)],
+ table_id=1,
+ )
+ ip_t01.add_vpp_config()
+ ip_t10.add_vpp_config()
+
+ # Start builtin server for ip6
+ self.logger.info(self.vapi.cli(server_cmd))
+
+ # case 1: No filter
+
+ # Not expecting an error
+ self.logger.info(self.vapi.cli(client_cmd))
+
+ # case 2: filter to block
+ # Add session filter to block loop0
+ rules = []
+ rules.append(
+ self.create_rule(lcl=self.loop0.local_ip6 + "/128", action_index=0, tag="")
+ )
+ self.apply_rules(rules, is_add=1, appns_index=0)
+ filter = self.vapi.session_sdl_dump()
+ self.assertEqual(filter[0].lcl, IPv6Network(self.loop0.local_ip6 + "/128"))
+
+ error = self.vapi.cli_return_response(client_cmd)
+ # Expecting an error because loop0 is blocked
+ self.assertEqual(-1, error.retval)
+
+ # case 3: remove to block
+ # Add session filter to block loop0
+ rules = []
+ rules.append(
+ self.create_rule(lcl=self.loop0.local_ip6 + "/128", action_index=0, tag="")
+ )
+ self.apply_rules(rules, is_add=0, appns_index=0)
+ # Not expecting an error
+ self.logger.info(self.vapi.cli(client_cmd))
+
+ # stop the server
+ self.logger.info(self.vapi.cli(server_cmd + " stop"))
+
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
+ # Delete inter-table routes
+ ip_t01.remove_vpp_config()
+ ip_t10.remove_vpp_config()
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+ )
+
+ def test_session_enable_disable(self):
+ """Session SDL enable/disable test"""
+
+ for i in range(10):
+ # Enable sdl
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_SDL
+ )
+
+ # Disable
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+ )
+
+ # Enable rule-table
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_RULE_TABLE
+ )
+
+ # Disable
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+ )
+
+ # Enable sdl
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_SDL
+ )
+
+ # Disable
+ self.vapi.session_enable_disable_v2(
+ rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+ )
+
+
+if __name__ == "__main__":
+ unittest.main(testRunner=VppTestRunner)
diff --git a/test/asf/test_tcp.py b/test/asf/test_tcp.py
index 69fc5c472a5..23772d34c76 100644
--- a/test/asf/test_tcp.py
+++ b/test/asf/test_tcp.py
@@ -4,8 +4,12 @@ import unittest
from asfframework import VppAsfTestCase, VppTestRunner
from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
+from config import config
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class TestTCP(VppAsfTestCase):
"""TCP Test Case"""
@@ -44,6 +48,12 @@ class TestTCP(VppAsfTestCase):
)
def tearDown(self):
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
for i in self.lo_interfaces:
i.unconfig_ip4()
i.set_table_ip4(0)
diff --git a/test/asf/test_tls.py b/test/asf/test_tls.py
index d2d1d9a4747..2ce87143339 100644
--- a/test/asf/test_tls.py
+++ b/test/asf/test_tls.py
@@ -91,6 +91,12 @@ class TestTLS(VppAsfTestCase):
)
def tearDown(self):
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
for i in self.lo_interfaces:
i.unconfig_ip4()
i.set_table_ip4(0)
diff --git a/test/asf/test_vcl.py b/test/asf/test_vcl.py
index a1113b863e8..124ea14089b 100644
--- a/test/asf/test_vcl.py
+++ b/test/asf/test_vcl.py
@@ -7,7 +7,7 @@ import subprocess
import signal
import glob
from config import config
-from asfframework import VppAsfTestCase, VppTestRunner, Worker
+from asfframework import VppAsfTestCase, VppTestRunner, Worker, tag_fixme_ubuntu2404
from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
iperf3 = "/usr/bin/iperf3"
@@ -189,6 +189,12 @@ class VCLTestCase(VppAsfTestCase):
self.logger.debug(self.vapi.cli("show ip fib"))
def thru_host_stack_tear_down(self):
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", secret=1234, sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="2", secret=5678, sw_if_index=self.loop1.sw_if_index
+ )
for i in self.lo_interfaces:
i.unconfig_ip4()
i.set_table_ip4(0)
@@ -240,6 +246,12 @@ class VCLTestCase(VppAsfTestCase):
self.logger.debug(self.vapi.cli("show ip6 fib"))
def thru_host_stack_ipv6_tear_down(self):
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", secret=1234, sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="2", secret=5678, sw_if_index=self.loop1.sw_if_index
+ )
for i in self.lo_interfaces:
i.unconfig_ip6()
i.set_table_ip6(0)
@@ -299,6 +311,7 @@ class VCLTestCase(VppAsfTestCase):
self.assert_equal(worker_client.result, 0, "Binary test return code")
+@tag_fixme_ubuntu2404
class LDPCutThruTestCase(VCLTestCase):
"""LDP Cut Thru Tests"""
@@ -403,6 +416,9 @@ class LDPCutThruTestCase(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLCutThruTestCase(VCLTestCase):
"""VCL Cut Thru Tests"""
@@ -489,6 +505,9 @@ class VCLCutThruTestCase(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackEcho(VCLTestCase):
"""VCL Thru Host Stack Echo"""
@@ -543,6 +562,9 @@ class VCLThruHostStackEcho(VCLTestCase):
self.logger.debug(self.vapi.cli("show app mq"))
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackTLS(VCLTestCase):
"""VCL Thru Host Stack TLS"""
@@ -594,6 +616,9 @@ class VCLThruHostStackTLS(VCLTestCase):
self.logger.debug(self.vapi.cli("show app mq"))
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackEchoInterruptMode(VCLThruHostStackEcho):
"""VCL Thru Host Stack Echo interrupt mode"""
@@ -625,6 +650,9 @@ class VCLThruHostStackTLSInterruptMode(VCLThruHostStackTLS):
super(VCLThruHostStackTLS, cls).setUpClass()
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackDTLS(VCLTestCase):
"""VCL Thru Host Stack DTLS"""
@@ -675,6 +703,9 @@ class VCLThruHostStackDTLS(VCLTestCase):
self.logger.debug(self.vapi.cli("show app mq"))
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackQUIC(VCLTestCase):
"""VCL Thru Host Stack QUIC"""
@@ -726,6 +757,62 @@ class VCLThruHostStackQUIC(VCLTestCase):
self.logger.debug(self.vapi.cli("show app mq"))
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
+class VCLThruHostStackHTTPPost(VCLTestCase):
+ """VCL Thru Host Stack HTTP Post"""
+
+ @classmethod
+ def setUpClass(cls):
+ cls.extra_vpp_plugin_config.append("plugin http_plugin.so { enable }")
+ super(VCLThruHostStackHTTPPost, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(VCLThruHostStackHTTPPost, cls).tearDownClass()
+
+ def setUp(self):
+ super(VCLThruHostStackHTTPPost, self).setUp()
+
+ self.thru_host_stack_setup()
+ self.client_uni_dir_http_post_timeout = 20
+ self.server_http_post_args = ["-p", "http", self.server_port]
+ self.client_uni_dir_http_post_test_args = [
+ "-N",
+ "10000",
+ "-U",
+ "-X",
+ "-p",
+ "http",
+ self.loop0.local_ip4,
+ self.server_port,
+ ]
+
+ def test_vcl_thru_host_stack_http_post_uni_dir(self):
+ """run VCL thru host stack uni-directional HTTP POST test"""
+
+ self.timeout = self.client_uni_dir_http_post_timeout
+ self.thru_host_stack_test(
+ "vcl_test_server",
+ self.server_http_post_args,
+ "vcl_test_client",
+ self.client_uni_dir_http_post_test_args,
+ )
+
+ def tearDown(self):
+ self.thru_host_stack_tear_down()
+ super(VCLThruHostStackHTTPPost, self).tearDown()
+
+ def show_commands_at_teardown(self):
+ self.logger.debug(self.vapi.cli("show app server"))
+ self.logger.debug(self.vapi.cli("show session verbose 2"))
+ self.logger.debug(self.vapi.cli("show app mq"))
+
+
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackBidirNsock(VCLTestCase):
"""VCL Thru Host Stack Bidir Nsock"""
@@ -780,6 +867,9 @@ class VCLThruHostStackBidirNsock(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class LDPThruHostStackBidirNsock(VCLTestCase):
"""LDP Thru Host Stack Bidir Nsock"""
@@ -830,6 +920,9 @@ class LDPThruHostStackBidirNsock(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class LDPThruHostStackNsock(VCLTestCase):
"""LDP Thru Host Stack Nsock"""
@@ -879,6 +972,9 @@ class LDPThruHostStackNsock(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLThruHostStackNsock(VCLTestCase):
"""VCL Thru Host Stack Nsock"""
@@ -928,6 +1024,7 @@ class VCLThruHostStackNsock(VCLTestCase):
)
+@tag_fixme_ubuntu2404
class LDPThruHostStackIperf(VCLTestCase):
"""LDP Thru Host Stack Iperf"""
@@ -975,6 +1072,7 @@ class LDPThruHostStackIperf(VCLTestCase):
)
+@tag_fixme_ubuntu2404
class LDPThruHostStackIperfUdp(VCLTestCase):
"""LDP Thru Host Stack Iperf UDP"""
@@ -1020,6 +1118,7 @@ class LDPThruHostStackIperfUdp(VCLTestCase):
)
+@tag_fixme_ubuntu2404
class LDPIpv6CutThruTestCase(VCLTestCase):
"""LDP IPv6 Cut Thru Tests"""
@@ -1125,6 +1224,9 @@ class LDPIpv6CutThruTestCase(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLIpv6CutThruTestCase(VCLTestCase):
"""VCL IPv6 Cut Thru Tests"""
@@ -1220,6 +1322,9 @@ class VCLIpv6CutThruTestCase(VCLTestCase):
)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class VCLIpv6ThruHostStackEcho(VCLTestCase):
"""VCL IPv6 Thru Host Stack Echo"""
diff --git a/test/asf/test_vhost.py b/test/asf/test_vhost.py
index 622716cafe3..f7cdecfa6de 100644
--- a/test/asf/test_vhost.py
+++ b/test/asf/test_vhost.py
@@ -5,8 +5,10 @@ import unittest
from asfframework import VppAsfTestCase, VppTestRunner
from vpp_vhost_interface import VppVhostInterface
+from config import config
+@unittest.skipIf("vhost" in config.excluded_plugins, "Exclude Vhost plugin tests")
class TesVhostInterface(VppAsfTestCase):
"""Vhost User Test Case"""
diff --git a/test/asf/vpp_session_sdl.py b/test/asf/vpp_session_sdl.py
new file mode 100644
index 00000000000..b10c11d1e15
--- /dev/null
+++ b/test/asf/vpp_session_sdl.py
@@ -0,0 +1,75 @@
+from ipaddress import IPv4Network
+
+from vpp_object import VppObject
+from vpp_papi import VppEnum
+from vpp_ip import INVALID_INDEX
+from vpp_papi_provider import UnexpectedApiReturnValueError
+
+
+class SessionSdl:
+ """Session SDL"""
+
+ def __init__(
+ self,
+ lcl,
+ action_index,
+ tag,
+ ):
+
+ self.action_index = action_index
+ self.lcl = lcl
+ self.tag = tag
+
+ def encode(self):
+ return {
+ "lcl": self.lcl,
+ "action_index": self.action_index,
+ "tag": self.tag,
+ }
+
+
+class VppSessionSdl(VppObject):
+ """VPP Session SDL"""
+
+ def __init__(self, test, rules, is_add, appns_index):
+ self._test = test
+ self._rules = rules
+ self.is_add = is_add
+ self.appns_index = appns_index
+
+ @property
+ def rules(self):
+ return self._rules
+
+ @property
+ def count(self):
+ return len(self._rules)
+
+ def encode_rules(self):
+ rules = []
+ for rule in self._rules:
+ rules.append(rule.encode())
+ return rules
+
+ def add_vpp_config(self, expect_error=False):
+ try:
+ reply = self._test.vapi.session_sdl_add_del(
+ is_add=self.is_add,
+ appns_index=self.appns_index,
+ count=self.count,
+ r=self.encode_rules(),
+ )
+ self._test.registry.register(self, self._test.logger)
+ if expect_error:
+ self._test.fail("Unexpected api reply")
+ return self
+ except UnexpectedApiReturnValueError:
+ if not expect_error:
+ self._test.fail("Unexpected api reply")
+ return None
+
+ def query_vpp_config(self):
+ pass
+
+ def remove_vpp_config(self, expect_error=False):
+ pass
diff --git a/test/config.py b/test/config.py
index 32cc4cac5fa..e939f188c6c 100644
--- a/test/config.py
+++ b/test/config.py
@@ -409,10 +409,11 @@ parser.add_argument(
"/var/run/user/${uid}/vpp.",
)
-default_decode_pcaps = False
+default_decode_pcaps = "failed"
parser.add_argument(
"--decode-pcaps",
- action="store_true",
+ action="store",
+ choices=["none", "failed", "all"],
default=default_decode_pcaps,
help=f"if set, decode all pcap files from a test run (default: {default_decode_pcaps})",
)
diff --git a/test/framework.py b/test/framework.py
index 6ff03d8b073..fc22ad6483c 100644
--- a/test/framework.py
+++ b/test/framework.py
@@ -71,6 +71,9 @@ class _PacketInfo(object):
#: Store the copy of the former packet.
data = None
+ def __repr__(self):
+ return f"_PacketInfo index:{self.index} src:{self.src} dst:{self.dst} ip:{self.ip} proto:{self.proto} data:{self.data}"
+
def __eq__(self, other):
index = self.index == other.index
src = self.src == other.src
diff --git a/test/patches/scapy-2.4.5/cdp.patch b/test/patches/scapy-2.4.5/cdp.patch
new file mode 100644
index 00000000000..8fa9f69cc80
--- /dev/null
+++ b/test/patches/scapy-2.4.5/cdp.patch
@@ -0,0 +1,14 @@
+diff --git a/scapy/contrib/cdp.py b/scapy/contrib/cdp.py
+index a1532b78..83963ff4 100644
+--- a/scapy/contrib/cdp.py
++++ b/scapy/contrib/cdp.py
+@@ -392,7 +392,7 @@ class _CDPChecksum:
+ This padding is only used for checksum computation. The original
+ packet should not be altered."""
+ if len(pkt) % 2:
+- last_chr = orb(pkt[-1])
++ last_chr = orb(pkt[len(pkt)-1:])
+ if last_chr <= 0x80:
+ return pkt[:-1] + b'\x00' + chb(last_chr)
+ else:
+
diff --git a/test/patches/scapy-2.4.5/ikev2.patch b/test/patches/scapy-2.4.5/ikev2.patch
new file mode 100644
index 00000000000..a1dd45ad661
--- /dev/null
+++ b/test/patches/scapy-2.4.5/ikev2.patch
@@ -0,0 +1,22 @@
+diff --git a/scapy/contrib/ikev2.py b/scapy/contrib/ikev2.py
+index 7799fd1e..f81af7ac 100644
+--- a/scapy/contrib/ikev2.py
++++ b/scapy/contrib/ikev2.py
+@@ -607,12 +607,16 @@ class IKEv2_payload_TSr(IKEv2_class):
+
+ class IKEv2_payload_Delete(IKEv2_class):
+ name = "IKEv2 Vendor ID"
++ name = "IKEv2 delete payload"
+ overload_fields = {IKEv2: {"next_payload": 42}}
+ fields_desc = [
+ ByteEnumField("next_payload", None, IKEv2_payload_type),
+ ByteField("res", 0),
+- FieldLenField("length", None, "vendorID", "H", adjust=lambda pkt, x:x + 4), # noqa: E501
+- StrLenField("vendorID", "", length_from=lambda x:x.length - 4),
++ FieldLenField("length", None, "SPIs", "H", adjust=lambda pkt, x:x + 8), # noqa: E501
++ ByteEnumField("proto", 1, {1: "IKEv2", 2: "AH", 3: "ESP"}),
++ ByteField("SPIsize", 0),
++ ShortField("SPInum", 0),
++ StrLenField("SPIs", "", length_from=lambda x: x.length - 8),
+ ]
+
diff --git a/test/patches/scapy-2.4.5/ipsec.patch b/test/patches/scapy-2.4.5/ipsec.patch
new file mode 100644
index 00000000000..46280ce23c4
--- /dev/null
+++ b/test/patches/scapy-2.4.5/ipsec.patch
@@ -0,0 +1,230 @@
+diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
+index 8251dc14..bbb71102 100644
+--- a/scapy/layers/ipsec.py
++++ b/scapy/layers/ipsec.py
+@@ -60,7 +60,7 @@ import scapy.modules.six as six
+ from scapy.modules.six.moves import range
+ from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \
+ IPv6ExtHdrRouting
+-
++from scapy.contrib.mpls import MPLS
+
+ ###############################################################################
+ class AH(Packet):
+@@ -360,13 +360,16 @@ class CryptAlgo(object):
+ encryptor = cipher.encryptor()
+
+ if self.is_aead:
+- if esn_en:
+- aad = struct.pack('!LLL', esp.spi, esn, esp.seq)
+- else:
+- aad = struct.pack('!LL', esp.spi, esp.seq)
++ aad = sa.build_aead(esp)
++ if self.name == 'AES-NULL-GMAC':
++ aad = aad + esp.iv + data
++ aes_null_gmac_data = data
++ data = b''
+ encryptor.authenticate_additional_data(aad)
+ data = encryptor.update(data) + encryptor.finalize()
+ data += encryptor.tag[:self.icv_size]
++ if self.name == 'AES-NULL-GMAC':
++ data = aes_null_gmac_data + data
+ else:
+ data = encryptor.update(data) + encryptor.finalize()
+
+@@ -402,16 +405,18 @@ class CryptAlgo(object):
+
+ if self.is_aead:
+ # Tag value check is done during the finalize method
+- if esn_en:
+- decryptor.authenticate_additional_data(
+- struct.pack('!LLL', esp.spi, esn, esp.seq))
+- else:
+- decryptor.authenticate_additional_data(
+- struct.pack('!LL', esp.spi, esp.seq))
++ aad = sa.build_aead(esp)
++ if self.name == 'AES-NULL-GMAC':
++ aad = aad + iv + data
++ aes_null_gmac_data = data
++ data = b''
++ decryptor.authenticate_additional_data(aad)
+ try:
+ data = decryptor.update(data) + decryptor.finalize()
+ except InvalidTag as err:
+ raise IPSecIntegrityError(err)
++ if self.name == 'AES-NULL-GMAC':
++ data = aes_null_gmac_data + data
+
+ # extract padlen and nh
+ padlen = orb(data[-2])
+@@ -458,6 +463,13 @@ if algorithms:
+ iv_size=8,
+ icv_size=16,
+ format_mode_iv=_salt_format_mode_iv)
++ CRYPT_ALGOS['AES-NULL-GMAC'] = CryptAlgo('AES-NULL-GMAC',
++ cipher=algorithms.AES,
++ mode=modes.GCM,
++ salt_size=4,
++ iv_size=8,
++ icv_size=16,
++ format_mode_iv=_salt_format_mode_iv)
+ if hasattr(modes, 'CCM'):
+ CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
+ cipher=algorithms.AES,
+@@ -546,7 +558,7 @@ class AuthAlgo(object):
+ else:
+ return self.mac(key, self.digestmod(), default_backend())
+
+- def sign(self, pkt, key, esn_en=False, esn=0):
++ def sign(self, pkt, key, trailer=None, esn_en=False, esn=0):
+ """
+ Sign an IPsec (ESP or AH) packet with this algo.
+
+@@ -565,20 +577,20 @@ class AuthAlgo(object):
+
+ if pkt.haslayer(ESP):
+ mac.update(raw(pkt[ESP]))
++ if trailer:
++ mac.update(trailer)
+ pkt[ESP].data += mac.finalize()[:self.icv_size]
+
+ elif pkt.haslayer(AH):
+ clone = zero_mutable_fields(pkt.copy(), sending=True)
+- if esn_en:
+- temp = raw(clone) + struct.pack('!L', esn)
+- else:
+- temp = raw(clone)
+- mac.update(temp)
++ mac.update(raw(clone))
++ if trailer:
++ mac.update(trailer)
+ pkt[AH].icv = mac.finalize()[:self.icv_size]
+
+ return pkt
+
+- def verify(self, pkt, key, esn_en=False, esn=0):
++ def verify(self, pkt, key, trailer, esn_en=False, esn=0):
+ """
+ Check that the integrity check value (icv) of a packet is valid.
+
+@@ -602,7 +614,6 @@ class AuthAlgo(object):
+ pkt_icv = pkt.data[len(pkt.data) - self.icv_size:]
+ clone = pkt.copy()
+ clone.data = clone.data[:len(clone.data) - self.icv_size]
+- temp = raw(clone)
+
+ elif pkt.haslayer(AH):
+ if len(pkt[AH].icv) != self.icv_size:
+@@ -611,12 +622,10 @@ class AuthAlgo(object):
+ pkt[AH].icv = pkt[AH].icv[:self.icv_size]
+ pkt_icv = pkt[AH].icv
+ clone = zero_mutable_fields(pkt.copy(), sending=False)
+- if esn_en:
+- temp = raw(clone) + struct.pack('!L', esn)
+- else:
+- temp = raw(clone)
+
+- mac.update(temp)
++ mac.update(raw(clone))
++ if trailer:
++ mac.update(trailer) # bytearray(4)) #raw(trailer))
+ computed_icv = mac.finalize()[:self.icv_size]
+
+ # XXX: Cannot use mac.verify because the ICV can be truncated
+@@ -805,7 +814,7 @@ class SecurityAssociation(object):
+ This class is responsible of "encryption" and "decryption" of IPsec packets. # noqa: E501
+ """
+
+- SUPPORTED_PROTOS = (IP, IPv6)
++ SUPPORTED_PROTOS = (IP, IPv6, MPLS)
+
+ def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None,
+ auth_algo=None, auth_key=None, tunnel_header=None, nat_t_header=None, esn_en=False, esn=0): # noqa: E501
+@@ -880,6 +889,23 @@ class SecurityAssociation(object):
+ raise TypeError('nat_t_header must be %s' % UDP.name)
+ self.nat_t_header = nat_t_header
+
++ def build_aead(self, esp):
++ if self.esn_en:
++ return (struct.pack('!LLL', esp.spi, self.seq_num >> 32, esp.seq))
++ else:
++ return (struct.pack('!LL', esp.spi, esp.seq))
++
++ def build_seq_num(self, num):
++ # only lower order bits are transmitted
++ # higher order bits are used in the ICV
++ lower = num & 0xffffffff
++ upper = num >> 32
++
++ if self.esn_en:
++ return lower, struct.pack("!I", upper)
++ else:
++ return lower, None
++
+ def check_spi(self, pkt):
+ if pkt.spi != self.spi:
+ raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' %
+@@ -893,7 +919,8 @@ class SecurityAssociation(object):
+ if len(iv) != self.crypt_algo.iv_size:
+ raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) # noqa: E501
+
+- esp = _ESPPlain(spi=self.spi, seq=seq_num or self.seq_num, iv=iv)
++ low_seq_num, high_seq_num = self.build_seq_num(seq_num or self.seq_num)
++ esp = _ESPPlain(spi=self.spi, seq=low_seq_num, iv=iv)
+
+ if self.tunnel_header:
+ tunnel = self.tunnel_header.copy()
+@@ -917,7 +944,7 @@ class SecurityAssociation(object):
+ esn_en=esn_en or self.esn_en,
+ esn=esn or self.esn)
+
+- self.auth_algo.sign(esp, self.auth_key)
++ self.auth_algo.sign(esp, self.auth_key, high_seq_num)
+
+ if self.nat_t_header:
+ nat_t_header = self.nat_t_header.copy()
+@@ -944,7 +971,8 @@ class SecurityAssociation(object):
+
+ def _encrypt_ah(self, pkt, seq_num=None, esn_en=False, esn=0):
+
+- ah = AH(spi=self.spi, seq=seq_num or self.seq_num,
++ low_seq_num, high_seq_num = self.build_seq_num(seq_num or self.seq_num)
++ ah = AH(spi=self.spi, seq=low_seq_num,
+ icv=b"\x00" * self.auth_algo.icv_size)
+
+ if self.tunnel_header:
+@@ -985,7 +1013,7 @@ class SecurityAssociation(object):
+ ip_header.plen = len(ip_header.payload) + len(ah) + len(payload)
+
+ signed_pkt = self.auth_algo.sign(ip_header / ah / payload,
+- self.auth_key,
++ self.auth_key, high_seq_num,
+ esn_en=esn_en or self.esn_en,
+ esn=esn or self.esn)
+
+@@ -1025,11 +1053,12 @@ class SecurityAssociation(object):
+
+ def _decrypt_esp(self, pkt, verify=True, esn_en=None, esn=None):
+
++ low_seq_num, high_seq_num = self.build_seq_num(self.seq_num)
+ encrypted = pkt[ESP]
+
+ if verify:
+ self.check_spi(pkt)
+- self.auth_algo.verify(encrypted, self.auth_key)
++ self.auth_algo.verify(encrypted, self.auth_key, high_seq_num)
+
+ esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key,
+ self.crypt_algo.icv_size or
+@@ -1070,9 +1099,10 @@ class SecurityAssociation(object):
+
+ def _decrypt_ah(self, pkt, verify=True, esn_en=None, esn=None):
+
++ low_seq_num, high_seq_num = self.build_seq_num(self.seq_num)
+ if verify:
+ self.check_spi(pkt)
+- self.auth_algo.verify(pkt, self.auth_key,
++ self.auth_algo.verify(pkt, self.auth_key, high_seq_num,
+ esn_en=esn_en or self.esn_en,
+ esn=esn or self.esn)
+
diff --git a/test/patches/scapy-2.4.5/ppp.patch b/test/patches/scapy-2.4.5/ppp.patch
new file mode 100644
index 00000000000..a3680bfee08
--- /dev/null
+++ b/test/patches/scapy-2.4.5/ppp.patch
@@ -0,0 +1,45 @@
+# NOTE: This patch copied from https://github.com/secdev/scapy
+# commit 3e6900776698cd5472c5405294414d5b672a3f18
+#
+diff --git a/scapy/layers/ppp.py b/scapy/layers/ppp.py
+index b5cd42b4..e0f4c593 100644
+--- a/scapy/layers/ppp.py
++++ b/scapy/layers/ppp.py
+@@ -292,6 +292,14 @@ class _PPPProtoField(EnumField):
+
+ See RFC 1661 section 2
+ <https://tools.ietf.org/html/rfc1661#section-2>
++
++ The generated proto field is two bytes when not specified, or when specified
++ as an integer or a string:
++ PPP()
++ PPP(proto=0x21)
++ PPP(proto="Internet Protocol version 4")
++ To explicitly forge a one byte proto field, use the bytes representation:
++ PPP(proto=b'\x21')
+ """
+ def getfield(self, pkt, s):
+ if ord(s[:1]) & 0x01:
+@@ -304,12 +312,18 @@ class _PPPProtoField(EnumField):
+ return super(_PPPProtoField, self).getfield(pkt, s)
+
+ def addfield(self, pkt, s, val):
+- if val < 0x100:
+- self.fmt = "!B"
+- self.sz = 1
++ if isinstance(val, bytes):
++ if len(val) == 1:
++ fmt, sz = "!B", 1
++ elif len(val) == 2:
++ fmt, sz = "!H", 2
++ else:
++ raise TypeError('Invalid length for PPP proto')
++ val = struct.Struct(fmt).unpack(val)[0]
+ else:
+- self.fmt = "!H"
+- self.sz = 2
++ fmt, sz = "!H", 2
++ self.fmt = fmt
++ self.sz = sz
+ self.struct = struct.Struct(self.fmt)
+ return super(_PPPProtoField, self).addfield(pkt, s, val)
diff --git a/test/patches/scapy-2.4.5/scapy-python312.patch b/test/patches/scapy-2.4.5/scapy-python312.patch
new file mode 100644
index 00000000000..f0638ff4dd6
--- /dev/null
+++ b/test/patches/scapy-2.4.5/scapy-python312.patch
@@ -0,0 +1,590 @@
+diff --git a/scapy/arch/bpf/core.py b/scapy/arch/bpf/core.py
+index d49267cd..13b7cff7 100644
+--- a/scapy/arch/bpf/core.py
++++ b/scapy/arch/bpf/core.py
+@@ -27,7 +27,7 @@ from scapy.error import Scapy_Exception, warning
+ from scapy.interfaces import InterfaceProvider, IFACES, NetworkInterface, \
+ network_name
+ from scapy.pton_ntop import inet_ntop
+-from scapy.modules.six.moves import range
++from six.moves import range
+
+
+ # ctypes definitions
+diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
+index 88f0de80..8870320f 100644
+--- a/scapy/arch/linux.py
++++ b/scapy/arch/linux.py
+@@ -49,8 +49,8 @@ from scapy.packet import Packet, Padding
+ from scapy.pton_ntop import inet_ntop
+ from scapy.supersocket import SuperSocket
+
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+ # Typing imports
+ from scapy.compat import (
+diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
+index 3e640f48..8a01681c 100755
+--- a/scapy/arch/windows/__init__.py
++++ b/scapy/arch/windows/__init__.py
+@@ -36,8 +36,8 @@ from scapy.pton_ntop import inet_ntop, inet_pton
+ from scapy.utils import atol, itom, mac2str, str2mac
+ from scapy.utils6 import construct_source_candidate_set, in6_getscope
+ from scapy.data import ARPHDR_ETHER, load_manuf
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import input, winreg
++import six
++from six.moves import input, winreg
+ from scapy.compat import plain_str
+ from scapy.supersocket import SuperSocket
+
+diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py
+index 45eea565..a694de0d 100644
+--- a/scapy/asn1/asn1.py
++++ b/scapy/asn1/asn1.py
+@@ -18,8 +18,8 @@ from scapy.error import Scapy_Exception, warning
+ from scapy.volatile import RandField, RandIP, GeneralizedTime
+ from scapy.utils import Enum_metaclass, EnumElement, binrepr
+ from scapy.compat import plain_str, chb, orb
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+
+ class RandASN1Object(RandField):
+diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py
+index 87679b55..887dd42e 100644
+--- a/scapy/asn1fields.py
++++ b/scapy/asn1fields.py
+@@ -20,8 +20,8 @@ from scapy.base_classes import BasePacket
+ from scapy.utils import binrepr
+ from scapy import packet
+ from functools import reduce
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+
+ class ASN1F_badsequence(Exception):
+diff --git a/scapy/base_classes.py b/scapy/base_classes.py
+index ec532110..51897d05 100644
+--- a/scapy/base_classes.py
++++ b/scapy/base_classes.py
+@@ -27,9 +27,9 @@ import warnings
+ import scapy
+ from scapy.error import Scapy_Exception
+ from scapy.consts import WINDOWS
+-import scapy.modules.six as six
++import six
+
+-from scapy.modules.six.moves import range
++from six.moves import range
+
+ from scapy.compat import (
+ Any,
+diff --git a/scapy/contrib/automotive/someip.py b/scapy/contrib/automotive/someip.py
+index a19e008d..136ab64c 100644
+--- a/scapy/contrib/automotive/someip.py
++++ b/scapy/contrib/automotive/someip.py
+@@ -36,7 +36,7 @@ from scapy.layers.inet import TCP, UDP
+ from scapy.layers.inet6 import IP6Field
+ from scapy.compat import raw, orb
+ from scapy.config import conf
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.packet import Packet, Raw, bind_top_down, bind_bottom_up
+ from scapy.fields import XShortField, BitEnumField, ConditionalField, \
+ BitField, XBitField, IntField, XByteField, ByteEnumField, \
+diff --git a/scapy/contrib/cansocket_python_can.py b/scapy/contrib/cansocket_python_can.py
+index 936d39cf..1a1b717d 100644
+--- a/scapy/contrib/cansocket_python_can.py
++++ b/scapy/contrib/cansocket_python_can.py
+@@ -21,7 +21,7 @@ from scapy.config import conf
+ from scapy.supersocket import SuperSocket
+ from scapy.layers.can import CAN
+ from scapy.error import warning
+-from scapy.modules.six.moves import queue
++from six.moves import queue
+ from scapy.compat import Any, List
+ from can import Message as can_Message
+ from can import CanError as can_CanError
+diff --git a/scapy/contrib/cdp.py b/scapy/contrib/cdp.py
+index fa116538..9700deba 100644
+--- a/scapy/contrib/cdp.py
++++ b/scapy/contrib/cdp.py
+@@ -43,7 +43,7 @@ from scapy.fields import (
+ from scapy.layers.inet import checksum
+ from scapy.layers.l2 import SNAP
+ from scapy.compat import orb, chb
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.config import conf
+
+
+diff --git a/scapy/contrib/diameter.py b/scapy/contrib/diameter.py
+index e99cb424..9fedca3d 100644
+--- a/scapy/contrib/diameter.py
++++ b/scapy/contrib/diameter.py
+@@ -32,8 +32,8 @@ from scapy.fields import ConditionalField, EnumField, Field, FieldLenField, \
+ XByteField, XIntField
+ from scapy.layers.inet import TCP
+ from scapy.layers.sctp import SCTPChunkData
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+ from scapy.compat import chb, orb, raw, bytes_hex, plain_str
+ from scapy.error import warning
+ from scapy.utils import inet_ntoa, inet_aton
+diff --git a/scapy/contrib/gtp.py b/scapy/contrib/gtp.py
+index bf369847..de3b8961 100644
+--- a/scapy/contrib/gtp.py
++++ b/scapy/contrib/gtp.py
+@@ -30,7 +30,7 @@ from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \
+ from scapy.layers.inet import IP, UDP
+ from scapy.layers.inet6 import IPv6, IP6Field
+ from scapy.layers.ppp import PPP
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.packet import bind_layers, bind_bottom_up, bind_top_down, \
+ Packet, Raw
+ from scapy.volatile import RandInt, RandIP, RandNum, RandString
+diff --git a/scapy/contrib/homeplugav.py b/scapy/contrib/homeplugav.py
+index 171eb7d0..92b22d16 100644
+--- a/scapy/contrib/homeplugav.py
++++ b/scapy/contrib/homeplugav.py
+@@ -44,7 +44,7 @@ from scapy.fields import (
+ XShortField,
+ )
+ from scapy.layers.l2 import Ether
+-from scapy.modules.six.moves import range
++from six.moves import range
+
+ """
+ Copyright (C) HomePlugAV Layer for Scapy by FlUxIuS (Sebastien Dudek)
+diff --git a/scapy/contrib/isis.py b/scapy/contrib/isis.py
+index b7fc222e..f68bb37d 100644
+--- a/scapy/contrib/isis.py
++++ b/scapy/contrib/isis.py
+@@ -80,7 +80,7 @@ from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol
+ from scapy.layers.inet6 import IP6ListField, IP6Field
+ from scapy.utils import fletcher16_checkbytes
+ from scapy.volatile import RandString, RandByte
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.compat import orb, hex_bytes
+
+ EXT_VERSION = "v0.0.3"
+diff --git a/scapy/contrib/isotp.py b/scapy/contrib/isotp.py
+index 845d566d..225c6b86 100644
+--- a/scapy/contrib/isotp.py
++++ b/scapy/contrib/isotp.py
+@@ -29,9 +29,9 @@ from scapy.fields import BitField, FlagsField, StrLenField, \
+ BitEnumField, ByteField, XByteField, BitFieldLenField, StrField
+ from scapy.compat import chb, orb
+ from scapy.layers.can import CAN, CAN_MAX_IDENTIFIER, CAN_MTU, CAN_MAX_DLEN
+-import scapy.modules.six as six
++import six
+ import scapy.automaton as automaton
+-from scapy.modules.six.moves import queue
++from six.moves import queue
+ from scapy.error import Scapy_Exception, warning, log_loading, log_runtime
+ from scapy.supersocket import SuperSocket, SO_TIMESTAMPNS
+ from scapy.config import conf
+diff --git a/scapy/contrib/ldp.py b/scapy/contrib/ldp.py
+index 25152ab7..ea8a6b83 100644
+--- a/scapy/contrib/ldp.py
++++ b/scapy/contrib/ldp.py
+@@ -27,7 +27,7 @@ from scapy.fields import BitField, IPField, IntField, ShortField, StrField, \
+ XBitField
+ from scapy.layers.inet import UDP
+ from scapy.layers.inet import TCP
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.config import conf
+ from scapy.utils import inet_aton, inet_ntoa
+
+diff --git a/scapy/contrib/lldp.py b/scapy/contrib/lldp.py
+index 04d37192..91b64439 100644
+--- a/scapy/contrib/lldp.py
++++ b/scapy/contrib/lldp.py
+@@ -51,7 +51,7 @@ from scapy.fields import MACField, IPField, BitField, \
+ ShortField, XStrLenField, ByteField, ConditionalField, \
+ MultipleTypeField
+ from scapy.packet import Packet, bind_layers
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.data import ETHER_TYPES
+ from scapy.compat import orb
+
+diff --git a/scapy/contrib/nfs.py b/scapy/contrib/nfs.py
+index 79259e39..6d6f47a1 100644
+--- a/scapy/contrib/nfs.py
++++ b/scapy/contrib/nfs.py
+@@ -12,7 +12,7 @@ from scapy.packet import Packet, bind_layers
+ from scapy.fields import IntField, IntEnumField, FieldListField, LongField, \
+ XIntField, XLongField, ConditionalField, PacketListField, StrLenField, \
+ PacketField
+-from scapy.modules.six import integer_types
++from six import integer_types
+
+ nfsstat3 = {
+ 0: 'NFS3_OK',
+diff --git a/scapy/contrib/ppi_geotag.py b/scapy/contrib/ppi_geotag.py
+index a7cc6345..bf16ae2b 100644
+--- a/scapy/contrib/ppi_geotag.py
++++ b/scapy/contrib/ppi_geotag.py
+@@ -34,8 +34,8 @@ from scapy.fields import ByteField, ConditionalField, Field, FlagsField, \
+ UTCTimeField, XLEIntField, SignedByteField, XLEShortField
+ from scapy.layers.ppi import PPI_Hdr, PPI_Element
+ from scapy.error import warning
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+ CURR_GEOTAG_VER = 2 # Major revision of specification
+
+diff --git a/scapy/contrib/skinny.py b/scapy/contrib/skinny.py
+index c12cb94e..f8a8be06 100644
+--- a/scapy/contrib/skinny.py
++++ b/scapy/contrib/skinny.py
+@@ -29,7 +29,7 @@ from scapy.packet import Packet, bind_layers
+ from scapy.fields import FlagsField, IPField, LEIntEnumField, LEIntField, \
+ StrFixedLenField
+ from scapy.layers.inet import TCP
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.volatile import RandShort
+ from scapy.config import conf
+
+diff --git a/scapy/contrib/tacacs.py b/scapy/contrib/tacacs.py
+index ed933f10..7b31f4c0 100755
+--- a/scapy/contrib/tacacs.py
++++ b/scapy/contrib/tacacs.py
+@@ -29,7 +29,7 @@ from scapy.fields import FieldLenField, ConditionalField, StrLenField
+ from scapy.layers.inet import TCP
+ from scapy.compat import chb, orb
+ from scapy.config import conf
+-from scapy.modules.six.moves import range
++from six.moves import range
+
+ SECRET = 'test'
+
+diff --git a/scapy/fields.py b/scapy/fields.py
+index 7448400c..5db02244 100644
+--- a/scapy/fields.py
++++ b/scapy/fields.py
+@@ -37,9 +37,9 @@ from scapy.utils6 import in6_6to4ExtractAddr, in6_isaddr6to4, \
+ in6_isaddrTeredo, in6_ptop, Net6, teredoAddrExtractInfo
+ from scapy.base_classes import Gen, Net, BasePacket, Field_metaclass
+ from scapy.error import warning
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
+-from scapy.modules.six import integer_types
++import six
++from six.moves import range
++from six import integer_types
+
+ # Typing imports
+ from scapy.compat import (
+diff --git a/scapy/interfaces.py b/scapy/interfaces.py
+index aae0c55a..75919bd2 100644
+--- a/scapy/interfaces.py
++++ b/scapy/interfaces.py
+@@ -17,8 +17,8 @@ from scapy.consts import WINDOWS
+ from scapy.utils import pretty_list
+ from scapy.utils6 import in6_isvalid
+
+-from scapy.modules.six.moves import UserDict
+-import scapy.modules.six as six
++from six.moves import UserDict
++import six
+
+ # Typing imports
+ import scapy
+diff --git a/scapy/layers/bluetooth4LE.py b/scapy/layers/bluetooth4LE.py
+index 40f8b0bb..d461b808 100644
+--- a/scapy/layers/bluetooth4LE.py
++++ b/scapy/layers/bluetooth4LE.py
+@@ -23,7 +23,7 @@ from scapy.contrib.ethercat import LEBitEnumField, LEBitField
+ from scapy.layers.bluetooth import EIR_Hdr, L2CAP_Hdr
+ from scapy.layers.ppi import PPI_Element, PPI_Hdr
+
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.utils import mac2str, str2mac
+
+ ####################
+diff --git a/scapy/layers/dhcp.py b/scapy/layers/dhcp.py
+index 4164d88e..ccb42e93 100644
+--- a/scapy/layers/dhcp.py
++++ b/scapy/layers/dhcp.py
+@@ -31,8 +31,8 @@ from scapy.volatile import RandBin, RandField, RandNum, RandNumExpo
+ from scapy.arch import get_if_raw_hwaddr
+ from scapy.sendrecv import srp1, sendp
+ from scapy.error import warning
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+ from scapy.config import conf
+
+ dhcpmagic = b"c\x82Sc"
+diff --git a/scapy/layers/dns.py b/scapy/layers/dns.py
+index b1c9f456..5e87ef5e 100755
+--- a/scapy/layers/dns.py
++++ b/scapy/layers/dns.py
+@@ -24,8 +24,8 @@ from scapy.sendrecv import sr1
+ from scapy.layers.inet import IP, DestIPField, IPField, UDP, TCP
+ from scapy.layers.inet6 import DestIP6Field, IP6Field
+ from scapy.error import log_runtime, warning, Scapy_Exception
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+
+ def dns_get_str(s, pointer=0, pkt=None, _fullpacket=False):
+diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py
+index 5222df51..2c411b81 100644
+--- a/scapy/layers/inet.py
++++ b/scapy/layers/inet.py
+@@ -64,8 +64,8 @@ from scapy.pton_ntop import inet_pton
+
+ import scapy.as_resolvers
+
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+ ####################
+ # IP Tools class #
+diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
+index 8251dc14..852b3dfb 100644
+--- a/scapy/layers/ipsec.py
++++ b/scapy/layers/ipsec.py
+@@ -56,8 +56,8 @@ from scapy.fields import ByteEnumField, ByteField, IntField, PacketField, \
+ ShortField, StrField, XIntField, XStrField, XStrLenField
+ from scapy.packet import Packet, bind_layers, Raw
+ from scapy.layers.inet import IP, UDP
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+ from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \
+ IPv6ExtHdrRouting
+
+diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
+index b1224208..85377b37 100644
+--- a/scapy/layers/l2.py
++++ b/scapy/layers/l2.py
+@@ -51,7 +51,7 @@ from scapy.fields import (
+ XShortEnumField,
+ XShortField,
+ )
+-from scapy.modules.six import viewitems
++from six import viewitems
+ from scapy.packet import bind_layers, Packet
+ from scapy.plist import (
+ PacketList,
+diff --git a/scapy/layers/ntp.py b/scapy/layers/ntp.py
+index 21da95c8..c705c96a 100644
+--- a/scapy/layers/ntp.py
++++ b/scapy/layers/ntp.py
+@@ -25,8 +25,8 @@ from scapy.layers.inet import UDP
+ from scapy.utils import lhex
+ from scapy.compat import orb
+ from scapy.config import conf
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+
+
+ #############################################################################
+diff --git a/scapy/layers/tftp.py b/scapy/layers/tftp.py
+index 2e3077d9..11f1ed94 100644
+--- a/scapy/layers/tftp.py
++++ b/scapy/layers/tftp.py
+@@ -16,7 +16,7 @@ from scapy.fields import PacketListField, ShortEnumField, ShortField, \
+ StrNullField
+ from scapy.automaton import ATMT, Automaton
+ from scapy.layers.inet import UDP, IP
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.config import conf
+ from scapy.volatile import RandShort
+
+diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py
+index b6eb0af2..293ef971 100644
+--- a/scapy/layers/tls/cert.py
++++ b/scapy/layers/tls/cert.py
+@@ -33,8 +33,8 @@ import os
+ import time
+
+ from scapy.config import conf, crypto_validator
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+ from scapy.error import warning
+ from scapy.utils import binrepr
+ from scapy.asn1.asn1 import ASN1_BIT_STRING
+diff --git a/scapy/layers/tls/crypto/prf.py b/scapy/layers/tls/crypto/prf.py
+index 210f9108..cb56f247 100644
+--- a/scapy/layers/tls/crypto/prf.py
++++ b/scapy/layers/tls/crypto/prf.py
+@@ -13,7 +13,7 @@ from scapy.utils import strxor
+
+ from scapy.layers.tls.crypto.hash import _tls_hash_algs
+ from scapy.layers.tls.crypto.h_mac import _tls_hmac_algs
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.compat import bytes_encode
+
+
+diff --git a/scapy/modules/krack/crypto.py b/scapy/modules/krack/crypto.py
+index a4803def..69cc678a 100644
+--- a/scapy/modules/krack/crypto.py
++++ b/scapy/modules/krack/crypto.py
+@@ -6,8 +6,8 @@ from zlib import crc32
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
+ from cryptography.hazmat.backends import default_backend
+
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+ from scapy.compat import orb, chb
+ from scapy.layers.dot11 import Dot11TKIP
+ from scapy.utils import mac2str
+diff --git a/scapy/modules/p0f.py b/scapy/modules/p0f.py
+index 74ee9f16..5bca0bec 100644
+--- a/scapy/modules/p0f.py
++++ b/scapy/modules/p0f.py
+@@ -24,7 +24,7 @@ from scapy.error import warning, Scapy_Exception, log_runtime
+ from scapy.volatile import RandInt, RandByte, RandNum, RandShort, RandString
+ from scapy.sendrecv import sniff
+ from scapy.modules import six
+-from scapy.modules.six.moves import map, range
++from six.moves import map, range
+ if conf.route is None:
+ # unused import, only to initialize conf.route
+ import scapy.route # noqa: F401
+diff --git a/scapy/modules/voip.py b/scapy/modules/voip.py
+index 420ed641..9aa3cceb 100644
+--- a/scapy/modules/voip.py
++++ b/scapy/modules/voip.py
+@@ -18,7 +18,7 @@ from scapy.layers.inet import IP, UDP
+ from scapy.layers.rtp import RTP
+ from scapy.consts import WINDOWS
+ from scapy.config import conf
+-from scapy.modules.six.moves import range
++from six.moves import range
+
+
+ sox_base = (["sox", "-t", ".ul"], ["-", "-t", "ossdsp", "/dev/dsp"])
+diff --git a/scapy/plist.py b/scapy/plist.py
+index 6e959f9b..b9ba2d47 100644
+--- a/scapy/plist.py
++++ b/scapy/plist.py
+@@ -27,8 +27,8 @@ from scapy.utils import do_graph, hexdump, make_table, make_lined_table, \
+ from scapy.extlib import plt, Line2D, \
+ MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS
+ from functools import reduce
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range, zip
++import six
++from six.moves import range, zip
+
+ # typings
+ from scapy.compat import (
+diff --git a/scapy/pton_ntop.py b/scapy/pton_ntop.py
+index ba023a77..fd3e2522 100644
+--- a/scapy/pton_ntop.py
++++ b/scapy/pton_ntop.py
+@@ -14,7 +14,7 @@ from __future__ import absolute_import
+ import socket
+ import re
+ import binascii
+-from scapy.modules.six.moves import range
++from six.moves import range
+ from scapy.compat import plain_str, hex_bytes, bytes_encode, bytes_hex
+
+ from scapy.compat import (
+diff --git a/scapy/scapypipes.py b/scapy/scapypipes.py
+index 1cbd43cb..e8553493 100644
+--- a/scapy/scapypipes.py
++++ b/scapy/scapypipes.py
+@@ -7,7 +7,7 @@ from __future__ import print_function
+ import socket
+ import subprocess
+
+-from scapy.modules.six.moves.queue import Queue, Empty
++from six.moves.queue import Queue, Empty
+ from scapy.pipetool import Source, Drain, Sink
+ from scapy.config import conf
+ from scapy.compat import raw
+diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
+index 503c6a3b..372668de 100644
+--- a/scapy/sendrecv.py
++++ b/scapy/sendrecv.py
+@@ -36,7 +36,7 @@ from scapy.plist import (
+ from scapy.error import log_runtime, log_interactive, Scapy_Exception
+ from scapy.base_classes import Gen, SetGen
+ from scapy.modules import six
+-from scapy.modules.six.moves import map
++from six.moves import map
+ from scapy.sessions import DefaultSession
+ from scapy.supersocket import SuperSocket, IterSocket
+
+diff --git a/scapy/tools/UTscapy.py b/scapy/tools/UTscapy.py
+index 18e01659..a045bd49 100644
+--- a/scapy/tools/UTscapy.py
++++ b/scapy/tools/UTscapy.py
+@@ -27,8 +27,8 @@ import warnings
+ import zlib
+
+ from scapy.consts import WINDOWS
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range
++import six
++from six.moves import range
+ from scapy.config import conf
+ from scapy.compat import base64_bytes, bytes_hex, plain_str
+ from scapy.themes import DefaultTheme, BlackAndWhite
+diff --git a/scapy/utils.py b/scapy/utils.py
+index 12747fa6..09002915 100644
+--- a/scapy/utils.py
++++ b/scapy/utils.py
+@@ -28,8 +28,8 @@ import time
+ import threading
+ import warnings
+
+-import scapy.modules.six as six
+-from scapy.modules.six.moves import range, input, zip_longest
++import six
++from six.moves import range, input, zip_longest
+
+ from scapy.config import conf
+ from scapy.consts import DARWIN, OPENBSD, WINDOWS
+diff --git a/scapy/volatile.py b/scapy/volatile.py
+index 5587c7ce..f7e1b326 100644
+--- a/scapy/volatile.py
++++ b/scapy/volatile.py
+@@ -21,7 +21,7 @@ import struct
+ from scapy.base_classes import Net
+ from scapy.compat import bytes_encode, chb, plain_str
+ from scapy.utils import corrupt_bits, corrupt_bytes
+-from scapy.modules.six.moves import range
++from six.moves import range
+
+ ####################
+ # Random numbers #
diff --git a/test/requirements-3.txt b/test/requirements-3.txt
index 101f9d880c7..2284a67e5a9 100644
--- a/test/requirements-3.txt
+++ b/test/requirements-3.txt
@@ -14,41 +14,41 @@ attrs==23.2.0 \
# via
# jsonschema
# referencing
-babel==2.14.0 \
- --hash=sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363 \
- --hash=sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287
+babel==2.15.0 \
+ --hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \
+ --hash=sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413
# via sphinx
-black==24.3.0 \
- --hash=sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f \
- --hash=sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93 \
- --hash=sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11 \
- --hash=sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0 \
- --hash=sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9 \
- --hash=sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5 \
- --hash=sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213 \
- --hash=sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d \
- --hash=sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7 \
- --hash=sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837 \
- --hash=sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f \
- --hash=sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395 \
- --hash=sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995 \
- --hash=sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f \
- --hash=sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597 \
- --hash=sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959 \
- --hash=sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5 \
- --hash=sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb \
- --hash=sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4 \
- --hash=sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7 \
- --hash=sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd \
- --hash=sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7
+black==24.4.2 \
+ --hash=sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474 \
+ --hash=sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1 \
+ --hash=sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0 \
+ --hash=sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8 \
+ --hash=sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96 \
+ --hash=sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1 \
+ --hash=sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04 \
+ --hash=sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021 \
+ --hash=sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94 \
+ --hash=sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d \
+ --hash=sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c \
+ --hash=sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7 \
+ --hash=sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c \
+ --hash=sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc \
+ --hash=sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7 \
+ --hash=sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d \
+ --hash=sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c \
+ --hash=sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741 \
+ --hash=sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce \
+ --hash=sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb \
+ --hash=sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063 \
+ --hash=sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e
# via -r requirements.txt
build==1.2.1 \
--hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \
--hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4
# via pip-tools
-certifi==2024.2.2 \
- --hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
- --hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
+certifi==2024.7.4 \
+ --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \
+ --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90
# via requests
cffi==1.16.0 \
--hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \
@@ -206,39 +206,34 @@ commonmark==0.9.1 \
--hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \
--hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9
# via recommonmark
-cryptography==42.0.5 \
- --hash=sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee \
- --hash=sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576 \
- --hash=sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d \
- --hash=sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30 \
- --hash=sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413 \
- --hash=sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb \
- --hash=sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da \
- --hash=sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4 \
- --hash=sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd \
- --hash=sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc \
- --hash=sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8 \
- --hash=sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1 \
- --hash=sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc \
- --hash=sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e \
- --hash=sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8 \
- --hash=sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940 \
- --hash=sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400 \
- --hash=sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7 \
- --hash=sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16 \
- --hash=sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278 \
- --hash=sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74 \
- --hash=sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec \
- --hash=sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1 \
- --hash=sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2 \
- --hash=sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c \
- --hash=sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922 \
- --hash=sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a \
- --hash=sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6 \
- --hash=sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1 \
- --hash=sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e \
- --hash=sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac \
- --hash=sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7
+cryptography==43.0.0 \
+ --hash=sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709 \
+ --hash=sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069 \
+ --hash=sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2 \
+ --hash=sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b \
+ --hash=sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e \
+ --hash=sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70 \
+ --hash=sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778 \
+ --hash=sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22 \
+ --hash=sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895 \
+ --hash=sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf \
+ --hash=sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431 \
+ --hash=sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f \
+ --hash=sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947 \
+ --hash=sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74 \
+ --hash=sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc \
+ --hash=sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66 \
+ --hash=sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66 \
+ --hash=sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf \
+ --hash=sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f \
+ --hash=sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5 \
+ --hash=sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e \
+ --hash=sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f \
+ --hash=sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55 \
+ --hash=sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1 \
+ --hash=sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47 \
+ --hash=sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5 \
+ --hash=sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0
# via
# -r requirements.txt
# noiseprotocol
@@ -253,17 +248,17 @@ docutils==0.20.1 \
# recommonmark
# sphinx
# sphinx-rtd-theme
-idna==3.6 \
- --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
- --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
+idna==3.7 \
+ --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
+ --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
# via requests
imagesize==1.4.1 \
--hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \
--hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a
# via sphinx
-importlib-metadata==7.1.0 \
- --hash=sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570 \
- --hash=sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2
+importlib-metadata==8.2.0 \
+ --hash=sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369 \
+ --hash=sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d
# via
# build
# sphinx
@@ -273,13 +268,13 @@ importlib-resources==6.4.0 \
# via
# jsonschema
# jsonschema-specifications
-jinja2==3.1.3 \
- --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \
- --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90
+jinja2==3.1.4 \
+ --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
+ --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
# via sphinx
-jsonschema==4.21.1 ; python_version >= "3.7" \
- --hash=sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f \
- --hash=sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5
+jsonschema==4.23.0 ; python_version >= "3.7" \
+ --hash=sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4 \
+ --hash=sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566
# via -r requirements.txt
jsonschema-specifications==2023.12.1 \
--hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \
@@ -362,9 +357,9 @@ objgraph==3.6.1 \
--hash=sha256:21c6bc62df0e7b77cc0a31d96feec04c965f09ec2e3d78b816b516a604f0defd \
--hash=sha256:fe96c74147bbcaae8665b396e5388bdcc3197deebba4e6381f05202ee5b453a7
# via -r requirements.txt
-packaging==24.0 \
- --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \
- --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9
+packaging==24.1 \
+ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
+ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
# via
# black
# build
@@ -382,35 +377,36 @@ pexpect==4.9.0 \
--hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \
--hash=sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f
# via -r requirements.txt
-pip-tools==7.3.0 \
- --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
- --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
+pip-tools==7.4.1 \
+ --hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \
+ --hash=sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9
# via -r requirements.txt
pkgutil-resolve-name==1.3.10 \
--hash=sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174 \
--hash=sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e
# via jsonschema
-platformdirs==4.2.0 \
- --hash=sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068 \
- --hash=sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768
+platformdirs==4.2.2 \
+ --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
+ --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
# via black
-psutil==5.9.8 \
- --hash=sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d \
- --hash=sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73 \
- --hash=sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8 \
- --hash=sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2 \
- --hash=sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e \
- --hash=sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36 \
- --hash=sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7 \
- --hash=sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c \
- --hash=sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee \
- --hash=sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421 \
- --hash=sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf \
- --hash=sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81 \
- --hash=sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0 \
- --hash=sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631 \
- --hash=sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4 \
- --hash=sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8
+psutil==6.0.0 \
+ --hash=sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35 \
+ --hash=sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0 \
+ --hash=sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c \
+ --hash=sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1 \
+ --hash=sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3 \
+ --hash=sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c \
+ --hash=sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd \
+ --hash=sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3 \
+ --hash=sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0 \
+ --hash=sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2 \
+ --hash=sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6 \
+ --hash=sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d \
+ --hash=sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c \
+ --hash=sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0 \
+ --hash=sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132 \
+ --hash=sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14 \
+ --hash=sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0
# via -r requirements.txt
ptyprocess==0.7.0 \
--hash=sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35 \
@@ -460,22 +456,24 @@ pyenchant==3.2.2 \
--hash=sha256:5facc821ece957208a81423af7d6ec7810dad29697cb0d77aae81e4e11c8e5a6 \
--hash=sha256:6153f521852e23a5add923dbacfbf4bebbb8d70c4e4bad609a8e0f9faeb915d1
# via sphinxcontrib-spelling
-pygments==2.17.2 \
- --hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \
- --hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367
+pygments==2.18.0 \
+ --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \
+ --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a
# via sphinx
-pympler==1.0.1 \
- --hash=sha256:993f1a3599ca3f4fcd7160c7545ad06310c9e12f70174ae7ae8d4e25f6c5d3fa \
- --hash=sha256:d260dda9ae781e1eab6ea15bacb84015849833ba5555f141d2d9b7b7473b307d
+pympler==1.1 \
+ --hash=sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424 \
+ --hash=sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506
# via -r requirements.txt
pyparsing==3.1.2 \
--hash=sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad \
--hash=sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742
# via -r requirements.txt
-pyproject-hooks==1.0.0 \
- --hash=sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8 \
- --hash=sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5
- # via build
+pyproject-hooks==1.1.0 \
+ --hash=sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965 \
+ --hash=sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2
+ # via
+ # build
+ # pip-tools
pytz==2024.1 \
--hash=sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812 \
--hash=sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319
@@ -537,125 +535,129 @@ recommonmark==0.7.1 \
--hash=sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f \
--hash=sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67
# via -r requirements.txt
-referencing==0.34.0 \
- --hash=sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844 \
- --hash=sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4
+referencing==0.35.1 \
+ --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \
+ --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de
# via
# jsonschema
# jsonschema-specifications
-requests==2.31.0 \
- --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
- --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
+requests==2.32.3 \
+ --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
+ --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
# via sphinx
-rpds-py==0.18.0 \
- --hash=sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f \
- --hash=sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c \
- --hash=sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76 \
- --hash=sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e \
- --hash=sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157 \
- --hash=sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f \
- --hash=sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5 \
- --hash=sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05 \
- --hash=sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24 \
- --hash=sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1 \
- --hash=sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8 \
- --hash=sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b \
- --hash=sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb \
- --hash=sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07 \
- --hash=sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1 \
- --hash=sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6 \
- --hash=sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e \
- --hash=sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e \
- --hash=sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1 \
- --hash=sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab \
- --hash=sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4 \
- --hash=sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17 \
- --hash=sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594 \
- --hash=sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d \
- --hash=sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d \
- --hash=sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3 \
- --hash=sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c \
- --hash=sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66 \
- --hash=sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f \
- --hash=sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80 \
- --hash=sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33 \
- --hash=sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f \
- --hash=sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c \
- --hash=sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022 \
- --hash=sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e \
- --hash=sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f \
- --hash=sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da \
- --hash=sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1 \
- --hash=sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688 \
- --hash=sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795 \
- --hash=sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c \
- --hash=sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98 \
- --hash=sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1 \
- --hash=sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20 \
- --hash=sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307 \
- --hash=sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4 \
- --hash=sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18 \
- --hash=sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294 \
- --hash=sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66 \
- --hash=sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467 \
- --hash=sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948 \
- --hash=sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e \
- --hash=sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1 \
- --hash=sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0 \
- --hash=sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7 \
- --hash=sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd \
- --hash=sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641 \
- --hash=sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d \
- --hash=sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9 \
- --hash=sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1 \
- --hash=sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da \
- --hash=sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3 \
- --hash=sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa \
- --hash=sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7 \
- --hash=sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40 \
- --hash=sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496 \
- --hash=sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124 \
- --hash=sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836 \
- --hash=sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434 \
- --hash=sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984 \
- --hash=sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f \
- --hash=sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6 \
- --hash=sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e \
- --hash=sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461 \
- --hash=sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c \
- --hash=sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432 \
- --hash=sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73 \
- --hash=sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58 \
- --hash=sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88 \
- --hash=sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337 \
- --hash=sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7 \
- --hash=sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863 \
- --hash=sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475 \
- --hash=sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3 \
- --hash=sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51 \
- --hash=sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf \
- --hash=sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024 \
- --hash=sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40 \
- --hash=sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9 \
- --hash=sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec \
- --hash=sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb \
- --hash=sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7 \
- --hash=sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861 \
- --hash=sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880 \
- --hash=sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f \
- --hash=sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd \
- --hash=sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca \
- --hash=sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58 \
- --hash=sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e
+rpds-py==0.19.1 \
+ --hash=sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd \
+ --hash=sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505 \
+ --hash=sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e \
+ --hash=sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5 \
+ --hash=sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a \
+ --hash=sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175 \
+ --hash=sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850 \
+ --hash=sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415 \
+ --hash=sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b \
+ --hash=sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94 \
+ --hash=sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523 \
+ --hash=sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b \
+ --hash=sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897 \
+ --hash=sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c \
+ --hash=sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5 \
+ --hash=sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c \
+ --hash=sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520 \
+ --hash=sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e \
+ --hash=sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f \
+ --hash=sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f \
+ --hash=sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde \
+ --hash=sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39 \
+ --hash=sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c \
+ --hash=sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65 \
+ --hash=sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4 \
+ --hash=sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1 \
+ --hash=sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401 \
+ --hash=sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace \
+ --hash=sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5 \
+ --hash=sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a \
+ --hash=sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a \
+ --hash=sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4 \
+ --hash=sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e \
+ --hash=sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14 \
+ --hash=sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165 \
+ --hash=sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693 \
+ --hash=sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d \
+ --hash=sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720 \
+ --hash=sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45 \
+ --hash=sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1 \
+ --hash=sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301 \
+ --hash=sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b \
+ --hash=sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972 \
+ --hash=sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67 \
+ --hash=sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963 \
+ --hash=sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5 \
+ --hash=sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5 \
+ --hash=sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38 \
+ --hash=sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23 \
+ --hash=sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76 \
+ --hash=sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3 \
+ --hash=sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f \
+ --hash=sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b \
+ --hash=sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1 \
+ --hash=sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e \
+ --hash=sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4 \
+ --hash=sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1 \
+ --hash=sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137 \
+ --hash=sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8 \
+ --hash=sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c \
+ --hash=sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec \
+ --hash=sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244 \
+ --hash=sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491 \
+ --hash=sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309 \
+ --hash=sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c \
+ --hash=sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e \
+ --hash=sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea \
+ --hash=sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed \
+ --hash=sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208 \
+ --hash=sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a \
+ --hash=sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c \
+ --hash=sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71 \
+ --hash=sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b \
+ --hash=sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859 \
+ --hash=sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687 \
+ --hash=sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e \
+ --hash=sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a \
+ --hash=sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58 \
+ --hash=sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c \
+ --hash=sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5 \
+ --hash=sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a \
+ --hash=sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f \
+ --hash=sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a \
+ --hash=sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705 \
+ --hash=sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474 \
+ --hash=sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2 \
+ --hash=sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51 \
+ --hash=sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866 \
+ --hash=sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301 \
+ --hash=sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb \
+ --hash=sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185 \
+ --hash=sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71 \
+ --hash=sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836 \
+ --hash=sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d \
+ --hash=sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd \
+ --hash=sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d \
+ --hash=sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8 \
+ --hash=sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b \
+ --hash=sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48 \
+ --hash=sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f \
+ --hash=sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c \
+ --hash=sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3 \
+ --hash=sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b
# via
# jsonschema
# referencing
-scapy==2.4.3 ; python_version >= "2.7" or python_version >= "3.4" \
- --hash=sha256:e2f8d11f6a941c14a789ae8b236b27bd634681f1b29b5e893861e284d234f6b0
+scapy==2.4.5 ; python_version >= "3.4" \
+ --hash=sha256:bc707e3604784496b6665a9e5b2a69c36cc9fb032af4864b29051531b24c8593
# via -r requirements.txt
-sh==2.0.6 \
- --hash=sha256:9b2998f313f201c777e2c0061f0b1367497097ef13388595be147e2a00bf7ba1 \
- --hash=sha256:ced8f2e081a858b66a46ace3703dec243779abbd5a1887ba7e3c34f34da70cd2
+sh==2.0.7 \
+ --hash=sha256:029d45198902bfb967391eccfd13a88d92f7cebd200411e93f99ebacc6afbb35 \
+ --hash=sha256:2f2f79a65abd00696cf2e9ad26508cf8abb6dba5745f40255f1c0ded2876926d
# via -r requirements.txt
six==1.16.0 \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
@@ -721,22 +723,21 @@ tomli==2.0.1 \
# black
# build
# pip-tools
- # pyproject-hooks
-typing-extensions==4.10.0 \
- --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \
- --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb
+typing-extensions==4.12.2 \
+ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
+ --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
# via black
-urllib3==2.2.1 \
- --hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \
- --hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19
+urllib3==2.2.2 \
+ --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
+ --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
# via requests
wheel==0.43.0 \
--hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \
--hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81
# via pip-tools
-zipp==3.18.1 \
- --hash=sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b \
- --hash=sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715
+zipp==3.19.2 \
+ --hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
+ --hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
# via
# importlib-metadata
# importlib-resources
diff --git a/test/requirements.txt b/test/requirements.txt
index c01d31161f7..d1ab9492ae8 100644
--- a/test/requirements.txt
+++ b/test/requirements.txt
@@ -1,4 +1,4 @@
-pip-tools==7.3.0 # BSD Keep this in sync with Makefile's PIP_TOOLS_VERSION
+pip-tools==7.4.1 # BSD Keep this in sync with Makefile's PIP_TOOLS_VERSION
cryptography!=2.0 # BSD/Apache-2.0
deprecation>=2.0.6 # Apache-2.0
faulthandler; python_version < '3.3' # # BSD License (2 clause)
@@ -6,7 +6,7 @@ ipaddress; python_version < '3.3' # PSF
parameterized>=0.6.1 # BSD
pexpect # ISC
psutil # BSD
-scapy==2.4.3; python_version >= '2.7' or python_version >= '3.4' # GPL2 https://github.com/secdev/scapy/blob/master/LICENSE
+scapy==2.4.5; python_version >= '3.4' # GPL2 https://github.com/secdev/scapy/blob/master/LICENSE
six # MIT
syslog_rfc5424_parser>=0.3.1 # ISC
objgraph # MIT
diff --git a/test/template_ipsec.py b/test/template_ipsec.py
index b5cd922f127..4e68d44013f 100644
--- a/test/template_ipsec.py
+++ b/test/template_ipsec.py
@@ -1,11 +1,13 @@
import unittest
import socket
import struct
+import re
+import os
from scapy.layers.inet import IP, ICMP, TCP, UDP
from scapy.layers.ipsec import SecurityAssociation, ESP
from scapy.layers.l2 import Ether
-from scapy.packet import raw, Raw
+from scapy.packet import raw, Raw, Padding
from scapy.layers.inet6 import (
IPv6,
ICMPv6EchoRequest,
@@ -22,8 +24,7 @@ from vpp_papi import VppEnum
from vpp_ipsec import VppIpsecSpd, VppIpsecSpdEntry, VppIpsecSpdItfBinding
from ipaddress import ip_address
-from re import search
-from os import popen
+from config import config
class IPsecIPv4Params:
@@ -323,6 +324,9 @@ class IpsecTcp(object):
self.assert_packet_checksums_valid(decrypted)
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class IpsecTcpTests(IpsecTcp):
def test_tcp_checksum(self):
"""verify checksum correctness for vpp generated packets"""
@@ -1849,6 +1853,9 @@ class IpsecTra4(object):
self._verify_tra_anti_replay_algorithm_no_esn()
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class IpsecTra4Tests(IpsecTra4):
"""UT test methods for Transport v4"""
@@ -1931,7 +1938,7 @@ class IpsecTra6(object):
Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac)
/ IPv6(src=src, dst=dst)
/ IPv6ExtHdrHopByHop()
- / IPv6ExtHdrFragment(id=2, offset=200)
+ / IPv6ExtHdrFragment(id=2, offset=0)
/ Raw(b"\xff" * 200)
for i in range(count)
]
@@ -1978,7 +1985,7 @@ class IpsecTra6(object):
tx = (
Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
/ IPv6(src=self.tra_if.local_ip6, dst=self.tra_if.remote_ip6)
- / IPv6ExtHdrFragment(id=2, offset=200)
+ / IPv6ExtHdrFragment(id=2, offset=0)
/ Raw(b"\xff" * 200)
)
@@ -1997,7 +2004,7 @@ class IpsecTra6(object):
Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
/ IPv6(src=self.tra_if.local_ip6, dst=self.tra_if.remote_ip6)
/ IPv6ExtHdrHopByHop()
- / IPv6ExtHdrFragment(id=2, offset=200)
+ / IPv6ExtHdrFragment(id=2, offset=0)
/ Raw(b"\xff" * 200)
)
@@ -2014,7 +2021,7 @@ class IpsecTra6(object):
Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
/ IPv6(src=self.tra_if.local_ip6, dst=self.tra_if.remote_ip6)
/ IPv6ExtHdrHopByHop()
- / IPv6ExtHdrFragment(id=2, offset=200)
+ / IPv6ExtHdrFragment(id=2, offset=0)
/ IPv6ExtHdrDestOpt()
/ Raw(b"\xff" * 200)
)
@@ -2029,6 +2036,9 @@ class IpsecTra6(object):
self.assert_equal(dc[IPv6ExtHdrFragment].id, 2)
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class IpsecTra6Tests(IpsecTra6):
"""UT test methods for Transport v6"""
@@ -3022,7 +3032,7 @@ class SpdFlowCacheTemplate(IPSecIPv4Fwd):
return False
def create_stream(
- cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=5678, proto="UDP-ESP"
+ cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=4500, proto="UDP-ESP"
):
packets = []
packets = super(SpdFlowCacheTemplate, cls).create_stream(
@@ -3031,7 +3041,7 @@ class SpdFlowCacheTemplate(IPSecIPv4Fwd):
return packets
def verify_capture(
- self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=5678
+ self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=4500
):
super(SpdFlowCacheTemplate, self).verify_l3_l4_capture(
src_if, dst_if, capture, tcp_port_in, udp_port_in
@@ -3056,7 +3066,7 @@ class SpdFastPathTemplate(IPSecIPv4Fwd):
super(SpdFastPathTemplate, self).tearDown()
def create_stream(
- cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=5678, proto="UDP-ESP"
+ cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=4500, proto="UDP-ESP"
):
packets = []
packets = super(SpdFastPathTemplate, cls).create_stream(
@@ -3065,7 +3075,7 @@ class SpdFastPathTemplate(IPSecIPv4Fwd):
return packets
def verify_capture(
- self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=5678
+ self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=4500
):
super(SpdFastPathTemplate, self).verify_l3_l4_capture(
src_if, dst_if, capture, tcp_port_in, udp_port_in
@@ -3084,7 +3094,7 @@ class IpsecDefaultTemplate(IPSecIPv4Fwd):
super(IpsecDefaultTemplate, self).tearDown()
def create_stream(
- cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=5678, proto="UDP-ESP"
+ cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=4500, proto="UDP-ESP"
):
packets = []
packets = super(IpsecDefaultTemplate, cls).create_stream(
@@ -3093,7 +3103,7 @@ class IpsecDefaultTemplate(IPSecIPv4Fwd):
return packets
def verify_capture(
- self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=5678
+ self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=4500
):
super(IpsecDefaultTemplate, self).verify_l3_l4_capture(
src_if, dst_if, capture, tcp_port_in, udp_port_in
diff --git a/test/test_arping.py b/test/test_arping.py
index a3e7e041ba1..435b15ee121 100644
--- a/test/test_arping.py
+++ b/test/test_arping.py
@@ -2,6 +2,9 @@ from scapy.layers.l2 import ARP
from scapy.layers.inet6 import ICMPv6ND_NS, ICMPv6ND_NA, IPv6
from framework import VppTestCase
+from config import config
+
+import unittest
""" TestArping is a subclass of VPPTestCase classes.
@@ -10,6 +13,7 @@ Basic test for sanity check of arping.
"""
+@unittest.skipIf("arping" in config.excluded_plugins, "Exclude Arping plugin tests")
class TestArping(VppTestCase):
"""Arping Test Case"""
diff --git a/test/test_bond.py b/test/test_bond.py
index ccd6246bed8..3c0df628470 100644
--- a/test/test_bond.py
+++ b/test/test_bond.py
@@ -10,6 +10,7 @@ from framework import VppTestCase
from asfframework import VppTestRunner
from vpp_bond_interface import VppBondInterface
from vpp_papi import MACAddress, VppEnum
+from config import config
class TestBondInterface(VppTestCase):
@@ -174,6 +175,9 @@ class TestBondInterface(VppTestCase):
bond0.remove_vpp_config()
+ @unittest.skipIf(
+ "lacp" in config.excluded_plugins, "Exclude tests requiring LACP plugin"
+ )
def test_bond_add_member(self):
"""Bond add_member/detach member test"""
@@ -227,6 +231,9 @@ class TestBondInterface(VppTestCase):
bond0.remove_vpp_config()
+ @unittest.skipIf(
+ "lacp" in config.excluded_plugins, "Exclude tests requiring LACP plugin"
+ )
def test_bond(self):
"""Bond add/delete interface test"""
self.logger.info("Bond add interfaces")
diff --git a/test/test_cdp.py b/test/test_cdp.py
index da378dbebe3..8d72e88aa24 100644
--- a/test/test_cdp.py
+++ b/test/test_cdp.py
@@ -17,6 +17,7 @@ from scapy.all import raw
from re import compile
from time import sleep
from util import ppp
+from config import config
import platform
import sys
import unittest
@@ -39,6 +40,7 @@ class CustomTLV(Packet):
]
+@unittest.skipIf("cdp" in config.excluded_plugins, "Exclude CDP plugin tests")
class TestCDP(VppTestCase):
"""CDP Test Case"""
diff --git a/test/test_cnat.py b/test/test_cnat.py
index ff8e1ebbbbb..9e979a4e09e 100644
--- a/test/test_cnat.py
+++ b/test/test_cnat.py
@@ -6,6 +6,7 @@ from framework import VppTestCase
from asfframework import VppTestRunner
from vpp_ip import INVALID_INDEX
from itertools import product
+from config import config
from scapy.packet import Raw
from scapy.layers.l2 import Ether
@@ -335,6 +336,7 @@ class CnatTestContext(object):
# -------------------------------------------------------------------
+@unittest.skipIf("cnat" in config.excluded_plugins, "Exclude CNAT plugin tests")
class TestCNatTranslation(CnatCommonTestCase):
"""CNat Translation"""
@@ -679,6 +681,7 @@ class TestCNatTranslation(CnatCommonTestCase):
self.cnat_fhc_translation()
+@unittest.skipIf("cnat" in config.excluded_plugins, "Exclude CNAT plugin tests")
class TestCNatSourceNAT(CnatCommonTestCase):
"""CNat Source NAT"""
@@ -818,6 +821,7 @@ class TestCNatSourceNAT(CnatCommonTestCase):
self.vapi.cnat_session_purge()
+@unittest.skipIf("cnat" in config.excluded_plugins, "Exclude CNAT plugin tests")
class TestCNatDHCP(CnatCommonTestCase):
"""CNat Translation"""
diff --git a/test/test_det44.py b/test/test_det44.py
index ede80981349..11d33ef0acd 100644
--- a/test/test_det44.py
+++ b/test/test_det44.py
@@ -12,8 +12,10 @@ from scapy.layers.inet import IP, TCP, UDP, ICMP
from scapy.layers.inet import IPerror, UDPerror
from scapy.layers.l2 import Ether
from util import ppp
+from config import config
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestDET44(VppTestCase):
"""Deterministic NAT Test Cases"""
diff --git a/test/test_dhcp.py b/test/test_dhcp.py
index 3924ebc840d..e668b7bd1c0 100644
--- a/test/test_dhcp.py
+++ b/test/test_dhcp.py
@@ -32,6 +32,7 @@ from vpp_papi import mac_pton, VppEnum
from vpp_sub_interface import VppDot1QSubint
from vpp_qos import VppQosEgressMap, VppQosMark
from vpp_dhcp import VppDHCPClient, VppDHCPProxy
+from config import config
DHCP4_CLIENT_PORT = 68
@@ -41,6 +42,7 @@ DHCP6_SERVER_PORT = 546
@tag_run_solo
+@unittest.skipIf("dhcp" in config.excluded_plugins, "Exclude DHCP plugin tests")
class TestDHCP(VppTestCase):
"""DHCP Test Case"""
@@ -119,7 +121,7 @@ class TestDHCP(VppTestCase):
for i in dhcp.options:
if isinstance(i, tuple):
- if i[0] == "relay_agent_Information":
+ if i[0] == "relay_agent_information":
#
# There are two sb-options present - each of length 6.
#
@@ -530,7 +532,7 @@ class TestDHCP(VppTestCase):
/ DHCP(
options=[
("message-type", "offer"),
- ("relay_agent_Information", option_82),
+ ("relay_agent_information", option_82),
("end"),
]
)
@@ -541,7 +543,7 @@ class TestDHCP(VppTestCase):
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
- rx = self.pg3.get_capture(1)
+ rx = self.pg3.get_capture(1, timeout=5)
rx = rx[0]
self.verify_dhcp_offer(rx, self.pg3)
@@ -561,7 +563,7 @@ class TestDHCP(VppTestCase):
/ DHCP(
options=[
("message-type", "offer"),
- ("relay_agent_Information", bad_ip),
+ ("relay_agent_information", bad_ip),
("end"),
]
)
@@ -582,7 +584,7 @@ class TestDHCP(VppTestCase):
/ DHCP(
options=[
("message-type", "offer"),
- ("relay_agent_Information", bad_if_index),
+ ("relay_agent_information", bad_if_index),
("end"),
]
)
@@ -746,7 +748,7 @@ class TestDHCP(VppTestCase):
/ DHCP(
options=[
("message-type", "offer"),
- ("relay_agent_Information", option_82),
+ ("relay_agent_information", option_82),
("end"),
]
)
@@ -759,7 +761,7 @@ class TestDHCP(VppTestCase):
/ DHCP(
options=[
("message-type", "offer"),
- ("relay_agent_Information", option_82),
+ ("relay_agent_information", option_82),
("end"),
]
)
@@ -786,7 +788,7 @@ class TestDHCP(VppTestCase):
/ DHCP(
options=[
("message-type", "offer"),
- ("relay_agent_Information", option_82),
+ ("relay_agent_information", option_82),
("end"),
]
)
diff --git a/test/test_dhcp6.py b/test/test_dhcp6.py
index 5c8e4354ab0..1328a62e1d3 100644
--- a/test/test_dhcp6.py
+++ b/test/test_dhcp6.py
@@ -24,14 +24,17 @@ from scapy.layers.inet6 import IPv6, Ether, UDP
from framework import VppTestCase
from asfframework import tag_fixme_vpp_workers, tag_run_solo
from vpp_papi import VppEnum
+from config import config
import util
import os
+import unittest
def ip6_normalize(ip6):
return inet_ntop(AF_INET6, inet_pton(AF_INET6, ip6))
+@unittest.skipIf("dhcp" in config.excluded_plugins, "Exclude DHCP plugin tests")
class TestDHCPv6DataPlane(VppTestCase):
"""DHCPv6 Data Plane Test Case"""
@@ -243,6 +246,7 @@ class TestDHCPv6DataPlane(VppTestCase):
@tag_run_solo
+@unittest.skipIf("dhcp" in config.excluded_plugins, "Exclude DHCP plugin tests")
class TestDHCPv6IANAControlPlane(VppTestCase):
"""DHCPv6 IA NA Control Plane Test Case"""
@@ -303,7 +307,7 @@ class TestDHCPv6IANAControlPlane(VppTestCase):
return addresses.difference(self.initial_addresses)
def validate_duid_ll(self, duid):
- DUID_LL(duid)
+ DUID_LL(bytes(duid))
def validate_packet(self, packet, msg_type, is_resend=False):
try:
@@ -497,6 +501,7 @@ class TestDHCPv6IANAControlPlane(VppTestCase):
@tag_fixme_vpp_workers
+@unittest.skipIf("dhcp" in config.excluded_plugins, "Exclude DHCP plugin tests")
class TestDHCPv6PDControlPlane(VppTestCase):
"""DHCPv6 PD Control Plane Test Case"""
@@ -557,7 +562,7 @@ class TestDHCPv6PDControlPlane(VppTestCase):
return addresses.difference(self.initial_addresses)
def validate_duid_ll(self, duid):
- DUID_LL(duid)
+ DUID_LL(bytes(duid))
def validate_packet(self, packet, msg_type, is_resend=False):
try:
diff --git a/test/test_dns.py b/test/test_dns.py
index edd1415bb01..77f26d71eaa 100644
--- a/test/test_dns.py
+++ b/test/test_dns.py
@@ -5,12 +5,14 @@ import unittest
from framework import VppTestCase
from asfframework import VppTestRunner
from ipaddress import *
+from config import config
from scapy.layers.inet import IP, UDP
from scapy.layers.l2 import Ether
from scapy.layers.dns import DNS, DNSQR
+@unittest.skipIf("dns" in config.excluded_plugins, "Exclude DNS plugin tests")
class TestDns(VppTestCase):
"""Dns Test Cases"""
diff --git a/test/test_dslite.py b/test/test_dslite.py
index ca481bc2d6b..b88605b3a2f 100644
--- a/test/test_dslite.py
+++ b/test/test_dslite.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import socket
+import unittest
from asfframework import tag_fixme_vpp_workers
from framework import VppTestCase
@@ -17,9 +18,11 @@ from scapy.packet import Raw
from syslog_rfc5424_parser import SyslogMessage, ParseError
from syslog_rfc5424_parser.constants import SyslogSeverity
from vpp_ip_route import VppIpRoute, VppRoutePath
+from config import config
@tag_fixme_vpp_workers
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestDSlite(VppTestCase):
"""DS-Lite Test Cases"""
@@ -233,6 +236,7 @@ class TestDSlite(VppTestCase):
self.logger.info(self.vapi.cli("show dslite sessions"))
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestDSliteCE(VppTestCase):
"""DS-Lite CE Test Cases"""
diff --git a/test/test_dvr.py b/test/test_dvr.py
index e616408e8c7..dcc17f040eb 100644
--- a/test/test_dvr.py
+++ b/test/test_dvr.py
@@ -13,10 +13,12 @@ from scapy.layers.l2 import Ether, Dot1Q
from scapy.layers.inet import IP, UDP
from socket import AF_INET
from ipaddress import IPv4Network
+from config import config
NUM_PKTS = 67
+@unittest.skipIf("acl" in config.excluded_plugins, "Exclude tests requiring ACL plugin")
class TestDVR(VppTestCase):
"""Distributed Virtual Router"""
diff --git a/test/test_flowprobe.py b/test/test_flowprobe.py
index 8e3fecfd7b4..9622e6158b5 100644
--- a/test/test_flowprobe.py
+++ b/test/test_flowprobe.py
@@ -31,6 +31,7 @@ from vpp_papi.macaddress import mac_ntop
from socket import inet_ntop
from vpp_papi import VppEnum
from vpp_sub_interface import VppDot1ADSubint
+from config import config
TMPL_COMMON_FIELD_COUNT = 6
@@ -183,9 +184,7 @@ class MethodHolder(VppTestCase):
variables and configure VPP.
"""
super(MethodHolder, cls).setUpClass()
- if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
- cls, "vpp"
- ):
+ if (is_distro_debian11 == True) and not hasattr(cls, "vpp"):
return
try:
# Create pg interfaces
@@ -386,6 +385,9 @@ class MethodHolder(VppTestCase):
@tag_fixme_vpp_workers
@tag_fixme_ubuntu2204
@tag_fixme_debian11
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class Flowprobe(MethodHolder):
"""Template verification, timer tests"""
@@ -1228,6 +1230,9 @@ class DatapathTestsHolder(object):
self.logger.info("FFP_TEST_FINISH_0002")
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class DatapathTx(MethodHolder, DatapathTestsHolder):
"""Collect info on Ethernet, IP4 and IP6 datapath (TX) (no timers)"""
@@ -1308,6 +1313,9 @@ class DatapathTx(MethodHolder, DatapathTestsHolder):
ipfix.remove_vpp_config()
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class DatapathRx(MethodHolder, DatapathTestsHolder):
"""Collect info on Ethernet, IP4 and IP6 datapath (RX) (no timers)"""
@@ -1318,6 +1326,9 @@ class DatapathRx(MethodHolder, DatapathTestsHolder):
@unittest.skipUnless(config.extended, "part of extended tests")
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class DisableIPFIX(MethodHolder):
"""Disable IPFIX"""
@@ -1366,6 +1377,9 @@ class DisableIPFIX(MethodHolder):
@unittest.skipUnless(config.extended, "part of extended tests")
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class ReenableIPFIX(MethodHolder):
"""Re-enable IPFIX"""
@@ -1433,6 +1447,9 @@ class ReenableIPFIX(MethodHolder):
@unittest.skipUnless(config.extended, "part of extended tests")
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class DisableFP(MethodHolder):
"""Disable Flowprobe feature"""
@@ -1541,6 +1558,9 @@ class DisableFP(MethodHolder):
@unittest.skipUnless(config.extended, "part of extended tests")
+@unittest.skipIf(
+ "flowprobe" in config.excluded_plugins, "Exclude Flowprobe plugin tests"
+)
class ReenableFP(MethodHolder):
"""Re-enable Flowprobe feature"""
diff --git a/test/test_geneve.py b/test/test_geneve.py
index 2b87303e710..f8c135bed10 100644
--- a/test/test_geneve.py
+++ b/test/test_geneve.py
@@ -5,6 +5,7 @@ import unittest
from framework import VppTestCase
from asfframework import VppTestRunner
from template_bd import BridgeDomain
+from config import config
from scapy.layers.l2 import Ether, ARP
from scapy.layers.inet import IP, UDP, ICMP
@@ -15,6 +16,7 @@ from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_ip import INVALID_INDEX
+@unittest.skipIf("geneve" in config.excluded_plugins, "Exclude GENEVE plugin tests")
class TestGeneve(BridgeDomain, VppTestCase):
"""GENEVE Test Case"""
@@ -250,6 +252,7 @@ class TestGeneve(BridgeDomain, VppTestCase):
self.logger.info(self.vapi.cli("show geneve tunnel"))
+@unittest.skipIf("geneve" in config.excluded_plugins, "Exclude GENEVE plugin tests")
class TestGeneveL3(VppTestCase):
"""GENEVE L3 Test Case"""
diff --git a/test/test_gre.py b/test/test_gre.py
index 763fb9d9b99..8b2851baea2 100644
--- a/test/test_gre.py
+++ b/test/test_gre.py
@@ -24,9 +24,11 @@ from vpp_ip_route import (
from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
from util import ppp, ppc
from vpp_papi import VppEnum
+from config import config
@tag_fixme_vpp_workers
+@unittest.skipIf("gre" in config.excluded_plugins, "Exclude GRE plugin tests")
class TestGREInputNodes(VppTestCase):
"""GRE Input Nodes Test Case"""
@@ -73,6 +75,7 @@ class TestGREInputNodes(VppTestCase):
self.assertEqual(err, err_count)
+@unittest.skipIf("gre" in config.excluded_plugins, "Exclude GRE plugin tests")
class TestGRE(VppTestCase):
"""GRE Test Case"""
diff --git a/test/test_gso.py b/test/test_gso.py
index 3d9ce5fb4ee..c3822b01faa 100644
--- a/test/test_gso.py
+++ b/test/test_gso.py
@@ -25,6 +25,7 @@ from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
from vpp_ipip_tun_interface import VppIpIpTunInterface
from vpp_vxlan_tunnel import VppVxlanTunnel
from vpp_gre_interface import VppGreInterface
+from config import config
from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect
from template_ipsec import (
@@ -378,6 +379,9 @@ class TestGSO(VppTestCase):
sw_if_index=self.pg1.sw_if_index, enable_disable=0
)
+ @unittest.skipIf(
+ "vxlan" in config.excluded_plugins, "Exclude tests requiring VXLAN plugin"
+ )
def test_gso_vxlan(self):
"""GSO VXLAN test"""
self.logger.info(self.vapi.cli("sh int addr"))
diff --git a/test/test_gtpu.py b/test/test_gtpu.py
index 5fe4f36ccb3..d05a1ff89e8 100644
--- a/test/test_gtpu.py
+++ b/test/test_gtpu.py
@@ -5,6 +5,7 @@ import unittest
from framework import VppTestCase
from asfframework import VppTestRunner, tag_fixme_vpp_workers
from template_bd import BridgeDomain
+from config import config
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
@@ -17,6 +18,7 @@ from vpp_ip import INVALID_INDEX
@tag_fixme_vpp_workers
+@unittest.skipIf("gtpu" in config.excluded_plugins, "Exclude GTPU plugin tests")
class TestGtpuUDP(VppTestCase):
"""GTPU UDP ports Test Case"""
@@ -119,6 +121,7 @@ class TestGtpuUDP(VppTestCase):
)
+@unittest.skipIf("gtpu" in config.excluded_plugins, "Exclude GTPU plugin tests")
class TestGtpu(BridgeDomain, VppTestCase):
"""GTPU Test Case"""
diff --git a/test/test_igmp.py b/test/test_igmp.py
index 037f108b897..e0da2b82079 100644
--- a/test/test_igmp.py
+++ b/test/test_igmp.py
@@ -19,6 +19,7 @@ from vpp_igmp import (
wait_for_igmp_event,
)
from vpp_ip_route import find_mroute, VppIpTable
+from config import config
class IgmpMode:
@@ -27,6 +28,7 @@ class IgmpMode:
@tag_fixme_vpp_workers
+@unittest.skipIf("igmp" in config.excluded_plugins, "Exclude IGMP plugin tests")
class TestIgmp(VppTestCase):
"""IGMP Test Case"""
diff --git a/test/test_ikev2.py b/test/test_ikev2.py
index fd065b47c98..4bff829c51b 100644
--- a/test/test_ikev2.py
+++ b/test/test_ikev2.py
@@ -23,10 +23,7 @@ from scapy.utils import long_converter
from framework import VppTestCase
from asfframework import (
tag_fixme_vpp_workers,
- tag_fixme_ubuntu2204,
- tag_fixme_debian11,
- is_distro_ubuntu2204,
- is_distro_debian11,
+ tag_fixme_ubuntu2404,
VppTestRunner,
)
from vpp_ikev2 import Profile, IDType, AuthMethod
@@ -677,7 +674,7 @@ class IkePeer(VppTestCase):
self.assertIsNotNone(self.p.query_vpp_config())
if self.sa.is_initiator:
self.sa.generate_dh_data()
- self.vapi.cli("ikev2 set logging level 4")
+ self.vapi.cli("ikev2 set logging level 5")
self.vapi.cli("event-lo clear")
def assert_counter(self, count, name, version="ip4"):
@@ -1215,7 +1212,7 @@ class TemplateInitiator(IkePeer):
if self.no_idr_auth:
self.assertEqual(idi.next_payload, 39) # AUTH
else:
- idr = ikev2.IKEv2_payload_IDr(idi.payload)
+ idr = ikev2.IKEv2_payload_IDr(bytes(idi.payload))
self.assertEqual(idr.load, self.sa.r_id)
prop = idi[ikev2.IKEv2_payload_Proposal]
c = self.sa.child_sas[0]
@@ -2025,20 +2022,21 @@ class TestApi(VppTestCase):
self.assertEqual(ap.tun_itf, 0xFFFFFFFF)
-@tag_fixme_vpp_workers
class TestResponderBehindNAT(TemplateResponder, Ikev2Params):
"""test responder - responder behind NAT"""
IKE_NODE_SUFFIX = "ip4-natt"
+ vpp_worker_count = 2
def config_tc(self):
self.config_params({"r_natt": True})
-@tag_fixme_vpp_workers
class TestInitiatorNATT(TemplateInitiator, Ikev2Params):
"""test ikev2 initiator - NAT traversal (intitiator behind NAT)"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params(
{
@@ -2067,10 +2065,11 @@ class TestInitiatorNATT(TemplateInitiator, Ikev2Params):
)
-@tag_fixme_vpp_workers
class TestInitiatorPsk(TemplateInitiator, Ikev2Params):
"""test ikev2 initiator - pre shared key auth"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params(
{
@@ -2098,10 +2097,11 @@ class TestInitiatorPsk(TemplateInitiator, Ikev2Params):
)
-@tag_fixme_vpp_workers
class TestInitiatorRequestWindowSize(TestInitiatorPsk):
"""test initiator - request window size (1)"""
+ vpp_worker_count = 2
+
def rekey_respond(self, req, update_child_sa_data):
ih = self.get_ike_header(req)
plain = self.sa.hmac_and_decrypt(ih)
@@ -2147,10 +2147,11 @@ class TestInitiatorRequestWindowSize(TestInitiatorPsk):
self.verify_ipsec_sas(is_rekey=True)
-@tag_fixme_vpp_workers
class TestInitiatorRekey(TestInitiatorPsk):
"""test ikev2 initiator - rekey"""
+ vpp_worker_count = 2
+
def rekey_from_initiator(self):
ispi = int.from_bytes(self.sa.child_sas[0].ispi, "little")
self.pg0.enable_capture()
@@ -2192,10 +2193,11 @@ class TestInitiatorRekey(TestInitiatorPsk):
self.verify_ipsec_sas(is_rekey=True)
-@tag_fixme_vpp_workers
class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params):
"""test ikev2 initiator - delete IKE SA from responder"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params(
{
@@ -2225,30 +2227,32 @@ class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params):
)
-@tag_fixme_vpp_workers
class TestResponderInitBehindNATT(TemplateResponder, Ikev2Params):
"""test ikev2 responder - initiator behind NAT"""
IKE_NODE_SUFFIX = "ip4-natt"
+ vpp_worker_count = 2
def config_tc(self):
self.config_params({"i_natt": True})
-@tag_fixme_vpp_workers
class TestResponderPsk(TemplateResponder, Ikev2Params):
"""test ikev2 responder - pre shared key auth"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params()
-@tag_fixme_vpp_workers
class TestResponderDpd(TestResponderPsk):
"""
Dead peer detection test
"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params({"dpd_disabled": False})
@@ -2276,11 +2280,11 @@ class TestResponderDpd(TestResponderPsk):
self.assertEqual(len(ipsec_sas), 0)
-@tag_fixme_vpp_workers
class TestResponderRekey(TestResponderPsk):
"""test ikev2 responder - rekey"""
WITH_KEX = False
+ vpp_worker_count = 2
def send_rekey_from_initiator(self):
if self.WITH_KEX:
@@ -2318,10 +2322,12 @@ class TestResponderRekey(TestResponderPsk):
self.assertEqual(r[0].sa.stats.n_rekey_req, 1)
-@tag_fixme_vpp_workers
+@tag_fixme_ubuntu2404
class TestResponderRekeyRepeat(TestResponderRekey):
"""test ikev2 responder - rekey repeat"""
+ vpp_worker_count = 2
+
def test_responder(self):
super(TestResponderRekeyRepeat, self).test_responder()
# rekey request is not accepted until old IPsec SA is expired
@@ -2344,24 +2350,26 @@ class TestResponderRekeyRepeat(TestResponderRekey):
self.verify_ipsec_sas(sa_count=3)
-@tag_fixme_vpp_workers
class TestResponderRekeyKEX(TestResponderRekey):
"""test ikev2 responder - rekey with key exchange"""
WITH_KEX = True
+ vpp_worker_count = 2
-@tag_fixme_vpp_workers
+@tag_fixme_ubuntu2404
class TestResponderRekeyRepeatKEX(TestResponderRekeyRepeat):
"""test ikev2 responder - rekey repeat with key exchange"""
WITH_KEX = True
+ vpp_worker_count = 2
-@tag_fixme_vpp_workers
class TestResponderRekeySA(TestResponderPsk):
"""test ikev2 responder - rekey IKE SA"""
+ vpp_worker_count = 2
+
def send_rekey_from_initiator(self, newsa):
packet = self.create_sa_rekey_request(
spi=newsa.ispi,
@@ -2402,8 +2410,6 @@ class TestResponderRekeySA(TestResponderPsk):
self.verify_ike_sas()
-@tag_fixme_ubuntu2204
-@tag_fixme_debian11
class TestResponderVrf(TestResponderPsk, Ikev2Params):
"""test ikev2 responder - non-default table id"""
@@ -2413,10 +2419,7 @@ class TestResponderVrf(TestResponderPsk, Ikev2Params):
globals()["ikev2"] = _ikev2
super(IkePeer, cls).setUpClass()
- if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
- cls, "vpp"
- ):
- return
+
cls.create_pg_interfaces(range(1))
cls.vapi.cli("ip table add 1")
cls.vapi.cli("set interface ip table pg0 1")
@@ -2431,7 +2434,7 @@ class TestResponderVrf(TestResponderPsk, Ikev2Params):
self.config_params({"dpd_disabled": False})
def test_responder(self):
- self.vapi.ikev2_profile_set_liveness(period=2, max_retries=1)
+ self.vapi.ikev2_profile_set_liveness(period=2, max_retries=3)
super(TestResponderVrf, self).test_responder()
self.pg0.enable_capture()
self.pg_start()
@@ -2442,10 +2445,11 @@ class TestResponderVrf(TestResponderPsk, Ikev2Params):
self.assertEqual(plain, b"")
-@tag_fixme_vpp_workers
class TestResponderRsaSign(TemplateResponder, Ikev2Params):
"""test ikev2 responder - cert based auth"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params(
{
@@ -2459,7 +2463,6 @@ class TestResponderRsaSign(TemplateResponder, Ikev2Params):
)
-@tag_fixme_vpp_workers
class Test_IKE_AES_CBC_128_SHA256_128_MODP2048_ESP_AES_CBC_192_SHA_384_192(
TemplateResponder, Ikev2Params
):
@@ -2467,6 +2470,8 @@ class Test_IKE_AES_CBC_128_SHA256_128_MODP2048_ESP_AES_CBC_192_SHA_384_192(
IKE:AES_CBC_128_SHA256_128,DH=modp2048 ESP:AES_CBC_192_SHA_384_192
"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params(
{
@@ -2481,7 +2486,6 @@ class Test_IKE_AES_CBC_128_SHA256_128_MODP2048_ESP_AES_CBC_192_SHA_384_192(
)
-@tag_fixme_vpp_workers
class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16(
TemplateResponder, Ikev2Params
):
@@ -2489,6 +2493,8 @@ class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16(
IKE:AES_CBC_128_SHA256_128,DH=modp3072 ESP:AES_GCM_16
"""
+ vpp_worker_count = 2
+
def config_tc(self):
self.config_params(
{
@@ -2501,13 +2507,13 @@ class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16(
)
-@tag_fixme_vpp_workers
class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params):
"""
IKE:AES_GCM_16_256
"""
IKE_NODE_SUFFIX = "ip6"
+ vpp_worker_count = 2
def config_tc(self):
self.config_params(
@@ -2524,12 +2530,13 @@ class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params):
)
-@tag_fixme_vpp_workers
class TestInitiatorKeepaliveMsg(TestInitiatorPsk):
"""
Test for keep alive messages
"""
+ vpp_worker_count = 2
+
def send_empty_req_from_responder(self):
packet = self.create_empty_request()
self.pg0.add_stream(packet)
diff --git a/test/test_interface_crud.py b/test/test_interface_crud.py
index c88759d9b59..6565d23a8b6 100644
--- a/test/test_interface_crud.py
+++ b/test/test_interface_crud.py
@@ -18,6 +18,7 @@ from scapy.layers.l2 import Ether
from framework import VppTestCase
from asfframework import VppTestRunner
+from config import config
class TestLoopbackInterfaceCRUD(VppTestCase):
@@ -81,6 +82,9 @@ class TestLoopbackInterfaceCRUD(VppTestCase):
info = (i.local_ip4, request_src_if.remote_ip4, 0, i.sw_if_index)
self.assertIn(info, rcvd_icmp_pkts)
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_crud(self):
# create
loopbacks = self.create_loopback_interfaces(20)
diff --git a/test/test_ip4.py b/test/test_ip4.py
index 926ca77a5f8..150ea629308 100644
--- a/test/test_ip4.py
+++ b/test/test_ip4.py
@@ -34,6 +34,7 @@ from vpp_papi import vpp_papi, VppEnum
from vpp_neighbor import VppNeighbor
from vpp_lo_interface import VppLoInterface
from vpp_policer import VppPolicer, PolicerAction
+from config import config
NUM_PKTS = 67
@@ -244,12 +245,13 @@ class TestIPv4RouteLookup(VppTestCase):
"""IPv4 Route Lookup Test Case"""
routes = []
+ tables = []
- def route_lookup(self, prefix, exact):
+ def route_lookup(self, prefix, exact, table_id=0):
return self.vapi.api(
self.vapi.papi.ip_route_lookup,
{
- "table_id": 0,
+ "table_id": table_id,
"exact": exact,
"prefix": prefix,
},
@@ -283,11 +285,30 @@ class TestIPv4RouteLookup(VppTestCase):
r.add_vpp_config()
self.routes.append(r)
+ custom_vrf = VppIpTable(self, 200)
+ custom_vrf.add_vpp_config()
+ self.tables.append(custom_vrf)
+
+ r = VppIpRoute(self, "2.2.0.0", 16, [drop_nh], 200)
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ r = VppIpRoute(self, "2.2.2.0", 24, [drop_nh], 200)
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ r = VppIpRoute(self, "2.2.2.2", 32, [drop_nh], 200)
+ r.add_vpp_config()
+ self.routes.append(r)
+
def tearDown(self):
# Remove the routes we added
for r in self.routes:
r.remove_vpp_config()
+ for vrf in self.tables:
+ vrf.remove_vpp_config()
+
super(TestIPv4RouteLookup, self).tearDown()
def test_exact_match(self):
@@ -305,6 +326,20 @@ class TestIPv4RouteLookup(VppTestCase):
with self.vapi.assert_negative_api_retval():
self.route_lookup("1.1.1.2/32", True)
+ # Verify we find the host route
+ prefix = "2.2.2.2/32"
+ result = self.route_lookup(prefix, True, 200)
+ assert prefix == str(result.route.prefix)
+
+ # Verify we find a middle prefix route
+ prefix = "2.2.2.0/24"
+ result = self.route_lookup(prefix, True, 200)
+ assert prefix == str(result.route.prefix)
+
+ # Verify we do not find an available LPM.
+ with self.vapi.assert_negative_api_retval():
+ self.route_lookup("2.2.2.1/32", True, 200)
+
def test_longest_prefix_match(self):
# verify we find lpm
lpm_prefix = "1.1.1.0/24"
@@ -315,6 +350,15 @@ class TestIPv4RouteLookup(VppTestCase):
result = self.route_lookup(lpm_prefix, False)
assert lpm_prefix == str(result.route.prefix)
+ # verify we find lpm
+ lpm_prefix = "2.2.2.0/24"
+ result = self.route_lookup("2.2.2.1/32", False, 200)
+ assert lpm_prefix == str(result.route.prefix)
+
+ # Verify we find the exact when not requested
+ result = self.route_lookup(lpm_prefix, False, 200)
+ assert lpm_prefix == str(result.route.prefix)
+
# Can't seem to delete the default route so no negative LPM test.
@@ -459,6 +503,9 @@ class TestIPv4IfAddrRoute(VppTestCase):
)
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class TestICMPEcho(VppTestCase):
"""ICMP Echo Test Case"""
@@ -929,6 +976,19 @@ class TestIPNull(VppTestCase):
r2.remove_vpp_config()
rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
+ t = VppIpTable(self, 2, False)
+ t.add_vpp_config()
+ r3 = VppIpRoute(
+ self,
+ "1.1.1.0",
+ 31,
+ [VppRoutePath("0.0.0.0", 0xFFFFFFFF, type=FibPathType.FIB_PATH_TYPE_DROP)],
+ table_id=2,
+ )
+ r3.add_vpp_config()
+ r3.remove_vpp_config()
+ t.remove_vpp_config()
+
class TestIPDisabled(VppTestCase):
"""IPv4 disabled"""
@@ -1396,7 +1456,7 @@ class TestIPLoadBalance(VppTestCase):
src_pkts.append(
(
Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
- / IP(dst="1.1.1.1", src="20.0.0.%d" % ii)
+ / IP(dst="1.1.1.1", src="20.0.0.%d" % (ii % 256))
/ UDP(sport=1234, dport=1234)
/ Raw(b"\xa5" * 100)
)
diff --git a/test/test_ip4_vrf_multi_instance.py b/test/test_ip4_vrf_multi_instance.py
index cbda790637b..318a4a81f44 100644
--- a/test/test_ip4_vrf_multi_instance.py
+++ b/test/test_ip4_vrf_multi_instance.py
@@ -195,7 +195,7 @@ class TestIp4VrfMultiInst(VppTestCase):
for i in range(count):
vrf_id = i + start
- self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id})
+ self.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": vrf_id})
self.logger.info("IPv4 VRF ID %d created" % vrf_id)
if vrf_id not in self.vrf_list:
self.vrf_list.append(vrf_id)
@@ -249,7 +249,7 @@ class TestIp4VrfMultiInst(VppTestCase):
self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
self.logger.debug(self.vapi.ppcli("show ip fib"))
self.logger.debug(self.vapi.ppcli("show ip neighbors"))
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": vrf_id})
def create_stream(self, src_if, packet_sizes):
"""
diff --git a/test/test_ip6.py b/test/test_ip6.py
index 84b060aa7a3..25f2c623a0b 100644
--- a/test/test_ip6.py
+++ b/test/test_ip6.py
@@ -67,6 +67,7 @@ from vpp_policer import VppPolicer, PolicerAction
from ipaddress import IPv6Network, IPv6Address
from vpp_gre_interface import VppGreInterface
from vpp_teib import VppTeib
+from config import config
AF_INET6 = socket.AF_INET6
@@ -1368,6 +1369,9 @@ class TestIPv6IfAddrRoute(VppTestCase):
)
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class TestICMPv6Echo(VppTestCase):
"""ICMPv6 Echo Test Case"""
@@ -3324,6 +3328,9 @@ class TestIP6AddrReplace(VppTestCase):
self.assertTrue(pfx.query_vpp_config())
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class TestIP6LinkLocal(VppTestCase):
"""IPv6 Link Local"""
@@ -3416,6 +3423,9 @@ class TestIP6LinkLocal(VppTestCase):
p_echo_request_3.dst = self.pg1.local_mac
self.send_and_expect(self.pg1, [p_echo_request_3], self.pg1)
+ @unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
+ )
def test_ip6_ll_p2p(self):
"""IPv6 Link Local P2P (GRE)"""
@@ -3445,6 +3455,9 @@ class TestIP6LinkLocal(VppTestCase):
self.pg0.unconfig_ip4()
gre_if.remove_vpp_config()
+ @unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
+ )
def test_ip6_ll_p2mp(self):
"""IPv6 Link Local P2MP (GRE)"""
diff --git a/test/test_ip6_nd_mirror_proxy.py b/test/test_ip6_nd_mirror_proxy.py
index 10dc77e1a86..e94309e8b50 100644
--- a/test/test_ip6_nd_mirror_proxy.py
+++ b/test/test_ip6_nd_mirror_proxy.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import unittest
-from socket import inet_pton, inet_ntop
+from socket import inet_pton, inet_ntop, AF_INET6
from framework import VppTestCase
from asfframework import VppTestRunner
@@ -59,8 +59,8 @@ class TestNDPROXY(VppTestCase):
# will come from the VPP's own address.
#
addr = self.pg0.remote_ip6
- nsma = in6_getnsma(inet_pton(socket.AF_INET6, addr))
- d = inet_ntop(socket.AF_INET6, nsma)
+ nsma = in6_getnsma(inet_pton(AF_INET6, addr))
+ d = inet_ntop(AF_INET6, nsma)
# Make pg1 un-numbered to pg0
#
diff --git a/test/test_ip6_vrf_multi_instance.py b/test/test_ip6_vrf_multi_instance.py
index da3de8e6100..26519b129cf 100644
--- a/test/test_ip6_vrf_multi_instance.py
+++ b/test/test_ip6_vrf_multi_instance.py
@@ -213,7 +213,7 @@ class TestIP6VrfMultiInst(VppTestCase):
"""
for i in range(count):
vrf_id = i + start
- self.vapi.ip_table_add_del(
+ self.vapi.ip_table_add_del_v2(
is_add=1, table={"table_id": vrf_id, "is_ip6": 1}
)
self.logger.info("IPv6 VRF ID %d created" % vrf_id)
@@ -276,7 +276,7 @@ class TestIP6VrfMultiInst(VppTestCase):
self.vrf_list.remove(vrf_id)
if vrf_id in self.vrf_reset_list:
self.vrf_reset_list.remove(vrf_id)
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id, "is_ip6": 1})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": vrf_id, "is_ip6": 1})
def create_stream(self, src_if, packet_sizes):
"""
diff --git a/test/test_ip_mcast.py b/test/test_ip_mcast.py
index 564b4231da9..682e7699210 100644
--- a/test/test_ip_mcast.py
+++ b/test/test_ip_mcast.py
@@ -14,6 +14,7 @@ from vpp_ip_route import (
)
from vpp_gre_interface import VppGreInterface
from vpp_papi import VppEnum
+from config import config
from scapy.packet import Raw
from scapy.layers.l2 import Ether, GRE
@@ -884,6 +885,9 @@ class TestIPMcast(VppTestCase):
signals = self.vapi.mfib_signal_dump()
self.assertEqual(0, len(signals))
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_ip_mcast_vrf(self):
"""IP Multicast Replication in non-default table"""
@@ -976,6 +980,9 @@ class TestIPMcast(VppTestCase):
self.send_and_expect(self.pg8, tx, self.pg8)
+ @unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
+ )
def test_ip_mcast_gre(self):
"""IP Multicast Replication over GRE"""
@@ -1056,6 +1063,9 @@ class TestIPMcast(VppTestCase):
self.assertEqual(rx[IP].dst, gre_if_3.t_dst)
self.assert_packet_checksums_valid(rx)
+ @unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
+ )
def test_ip6_mcast_gre(self):
"""IP6 Multicast Replication over GRE"""
diff --git a/test/test_ip_session_redirect.py b/test/test_ip_session_redirect.py
index 620b21626a9..3fed62bfade 100644
--- a/test/test_ip_session_redirect.py
+++ b/test/test_ip_session_redirect.py
@@ -13,8 +13,13 @@ from vpp_papi import VppEnum
from vpp_ip_route import VppRoutePath
from framework import VppTestCase
+from config import config
+@unittest.skipIf(
+ "ip_session_redirect" in config.excluded_plugins,
+ "Exclude IP session redirect plugin tests",
+)
class TestIpSessionRedirect(VppTestCase):
"""IP session redirect Test Case"""
diff --git a/test/test_ipip.py b/test/test_ipip.py
index 9574c2c299c..85b35fa7f24 100644
--- a/test/test_ipip.py
+++ b/test/test_ipip.py
@@ -88,7 +88,7 @@ class TestIPIP(VppTestCase):
self.table.remove_vpp_config()
def validate(self, rx, expected):
- self.assertEqual(rx, expected.__class__(expected))
+ self.assertTrue(bytes(rx), bytes(expected))
def generate_ip4_frags(self, payload_length, fragment_size):
p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
@@ -764,7 +764,7 @@ class TestIPIP6(VppTestCase):
rv = self.vapi.ipip_del_tunnel(sw_if_index=self.tunnel_if_index)
def validate(self, rx, expected):
- self.assertEqual(rx, expected.__class__(expected))
+ self.assertTrue(bytes(rx), bytes(expected))
def generate_ip6_frags(self, payload_length, fragment_size):
p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
diff --git a/test/test_ipsec_ah.py b/test/test_ipsec_ah.py
index 8fece500907..bc31a87abb0 100644
--- a/test/test_ipsec_ah.py
+++ b/test/test_ipsec_ah.py
@@ -28,6 +28,7 @@ from vpp_ipsec import VppIpsecSA, VppIpsecSpd, VppIpsecSpdEntry, VppIpsecSpdItfB
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_ip import DpoProto
from vpp_papi import VppEnum
+from config import config
class ConfigIpsecAH(TemplateIpsec):
@@ -526,6 +527,9 @@ class TestIpsecAhAll(ConfigIpsecAH, IpsecTra4, IpsecTra6, IpsecTun4, IpsecTun6):
def tearDown(self):
super(TestIpsecAhAll, self).tearDown()
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_integ_algs(self):
"""All Engines SHA[1_96, 256, 384, 512] w/ & w/o ESN"""
# foreach VPP crypto engine
diff --git a/test/test_ipsec_esp.py b/test/test_ipsec_esp.py
index 4e1957d8b53..bfdef2a78d7 100644
--- a/test/test_ipsec_esp.py
+++ b/test/test_ipsec_esp.py
@@ -1,4 +1,5 @@
import socket
+import unittest
from scapy.layers.ipsec import ESP
from scapy.layers.inet import IP, ICMP, UDP
from scapy.layers.inet6 import IPv6
@@ -27,6 +28,7 @@ from vpp_ipsec import VppIpsecSpd, VppIpsecSpdEntry, VppIpsecSA, VppIpsecSpdItfB
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_ip import DpoProto
from vpp_papi import VppEnum
+from config import config
NUM_PKTS = 67
engines_supporting_chain_bufs = ["openssl", "async"]
@@ -1075,6 +1077,9 @@ class MyParameters:
}
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class RunTestIpsecEspAll(ConfigIpsecESP, IpsecTra4, IpsecTra6, IpsecTun4, IpsecTun6):
"""Ipsec ESP all Algos"""
diff --git a/test/test_ipsec_spd_flow_cache_input.py b/test/test_ipsec_spd_flow_cache_input.py
index 283f345be18..b913a980599 100644
--- a/test/test_ipsec_spd_flow_cache_input.py
+++ b/test/test_ipsec_spd_flow_cache_input.py
@@ -785,9 +785,9 @@ class IPSec4SpdTestCaseCollisionInbound(SpdFlowCacheInbound):
# create the packet streams
# packet hashes to:
# ad727628
- packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1)
+ packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 4500)
# b5512898
- packets2 = self.create_stream(self.pg0, self.pg3, pkt_count, 1, 1)
+ packets2 = self.create_stream(self.pg0, self.pg3, pkt_count, 1, 4500)
# add the streams to the source interfaces
self.pg2.add_stream(packets1)
self.pg0.add_stream(packets2)
@@ -821,9 +821,9 @@ class IPSec4SpdTestCaseCollisionInbound(SpdFlowCacheInbound):
# create the packet streams
# 2f8f90f557eef12c
- packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1)
+ packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 4500)
# 6b7f9987719ffc1c
- packets2 = self.create_stream(self.pg3, self.pg2, pkt_count, 1, 1)
+ packets2 = self.create_stream(self.pg3, self.pg2, pkt_count, 1, 4500)
# add the streams to the source interfaces
self.pg2.add_stream(packets1)
self.pg3.add_stream(packets2)
diff --git a/test/test_ipsec_spd_fp_input.py b/test/test_ipsec_spd_fp_input.py
index ec4a7c71d7b..eb04df49244 100644
--- a/test/test_ipsec_spd_fp_input.py
+++ b/test/test_ipsec_spd_fp_input.py
@@ -8,6 +8,7 @@ from template_ipsec import IPSecIPv4Fwd
from template_ipsec import IPSecIPv6Fwd
from test_ipsec_esp import TemplateIpsecEsp
from template_ipsec import SpdFastPathTemplate
+from config import config
def debug_signal_handler(signal, frame):
@@ -242,6 +243,9 @@ class IPSec4SpdTestCaseDiscard(SpdFastPathInbound):
self.verify_policy_match(0, policy_1)
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class IPSec4SpdTestCaseProtect(SpdFastPathInboundProtect):
""" IPSec/IPv4 inbound: Policy mode test case with fast path \
(add protect)"""
@@ -830,6 +834,9 @@ class IPSec4SpdTestCaseMultiple(SpdFastPathInbound):
self.verify_policy_match(0, policy_22)
+@unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+)
class IPSec6SpdTestCaseProtect(SpdFastPathIPv6InboundProtect):
""" IPSec/IPv6 inbound: Policy mode test case with fast path \
(add protect)"""
diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py
index a7f91b9e967..cda52dc64c2 100644
--- a/test/test_ipsec_tun_if_esp.py
+++ b/test/test_ipsec_tun_if_esp.py
@@ -4,7 +4,7 @@ import copy
from scapy.layers.ipsec import SecurityAssociation, ESP
from scapy.layers.l2 import Ether, GRE, Dot1Q
-from scapy.packet import Raw, bind_layers
+from scapy.packet import Raw, bind_layers, Padding
from scapy.layers.inet import IP, UDP, ICMP
from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest
from scapy.contrib.mpls import MPLS
@@ -41,6 +41,7 @@ from vpp_papi import VppEnum
from vpp_papi_provider import CliFailedCommandError
from vpp_acl import AclRule, VppAcl, VppAclInterface
from vpp_policer import PolicerAction, VppPolicer, Dir
+from config import config
def config_tun_params(p, encryption_type, tun_if, src=None, dst=None):
@@ -1309,6 +1310,9 @@ class TestIpsec6MultiTunIfEsp(TemplateIpsec6TunProtect, TemplateIpsec, IpsecTun6
self.assertEqual(p.tun_if.get_tx_stats(), 127)
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGreTebIfEsp(TemplateIpsec, IpsecTun4Tests):
"""Ipsec GRE TEB ESP - TUN tests"""
@@ -1433,6 +1437,9 @@ class TestIpsecGreTebIfEsp(TemplateIpsec, IpsecTun4Tests):
super(TestIpsecGreTebIfEsp, self).tearDown()
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGreTebVlanIfEsp(TemplateIpsec, IpsecTun4Tests):
"""Ipsec GRE TEB ESP - TUN tests"""
@@ -1568,6 +1575,9 @@ class TestIpsecGreTebVlanIfEsp(TemplateIpsec, IpsecTun4Tests):
self.pg1_11.remove_vpp_config()
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGreTebIfEspTra(TemplateIpsec, IpsecTun4Tests):
"""Ipsec GRE TEB ESP - Tra tests"""
@@ -1686,6 +1696,9 @@ class TestIpsecGreTebIfEspTra(TemplateIpsec, IpsecTun4Tests):
super(TestIpsecGreTebIfEspTra, self).tearDown()
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGreTebUdpIfEspTra(TemplateIpsec, IpsecTun4Tests):
"""Ipsec GRE TEB UDP ESP - Tra tests"""
@@ -1819,6 +1832,9 @@ class TestIpsecGreTebUdpIfEspTra(TemplateIpsec, IpsecTun4Tests):
super(TestIpsecGreTebUdpIfEspTra, self).tearDown()
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGreIfEsp(TemplateIpsec, IpsecTun4Tests):
"""Ipsec GRE ESP - TUN tests"""
@@ -1931,6 +1947,9 @@ class TestIpsecGreIfEsp(TemplateIpsec, IpsecTun4Tests):
super(TestIpsecGreIfEsp, self).tearDown()
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGreIfEspTra(TemplateIpsec, IpsecTun4Tests):
"""Ipsec GRE ESP - TRA tests"""
@@ -2060,6 +2079,9 @@ class TestIpsecGreIfEspTra(TemplateIpsec, IpsecTun4Tests):
self.assertEqual(err, 1)
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecGre6IfEspTra(TemplateIpsec, IpsecTun6Tests):
"""Ipsec GRE ESP - TRA tests"""
@@ -2174,6 +2196,9 @@ class TestIpsecGre6IfEspTra(TemplateIpsec, IpsecTun6Tests):
super(TestIpsecGre6IfEspTra, self).tearDown()
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4):
"""Ipsec mGRE ESP v4 TRA tests"""
@@ -2329,6 +2354,9 @@ class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4):
self.verify_tun_44(p, count=N_PKTS)
+@unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests depending on GRE plugin"
+)
class TestIpsecMGreIfEspTra6(TemplateIpsec, IpsecTun6):
"""Ipsec mGRE ESP v6 TRA tests"""
@@ -2591,6 +2619,9 @@ class TestIpsec4TunProtectUdpTfc(TemplateIpsec4TunTfc, TestIpsec4TunProtectUdp):
@tag_fixme_vpp_workers
+@unittest.skipIf(
+ "acl" in config.excluded_plugins, "Exclude tests depending on ACL plugin"
+)
class TestIpsec4TunProtectTun(TemplateIpsec, TemplateIpsec4TunProtect, IpsecTun4):
"""IPsec IPv4 Tunnel protect - tunnel mode"""
@@ -3607,6 +3638,9 @@ class TestIpsecItf6Tfc(TemplateIpsec6TunTfc, TestIpsecItf6):
"""IPsec Interface IPv6 with TFC"""
+@unittest.skipIf(
+ "acl" in config.excluded_plugins, "Exclude tests depending on ACL plugin"
+)
class TestIpsecMIfEsp4(TemplateIpsec, IpsecTun4):
"""Ipsec P2MP ESP v4 tests"""
diff --git a/test/test_l2tp.py b/test/test_l2tp.py
index 172b0b8e0b8..34022629cc6 100644
--- a/test/test_l2tp.py
+++ b/test/test_l2tp.py
@@ -5,9 +5,13 @@ from scapy.layers.inet6 import IPv6
from asfframework import tag_fixme_vpp_workers
from framework import VppTestCase
+from config import config
+
+import unittest
@tag_fixme_vpp_workers
+@unittest.skipIf("l2tp" in config.excluded_plugins, "Exclude L2TP plugin tests")
class TestL2tp(VppTestCase):
"""L2TP Test Case"""
diff --git a/test/test_l3xc.py b/test/test_l3xc.py
index 69267b35817..d94292e5be4 100644
--- a/test/test_l3xc.py
+++ b/test/test_l3xc.py
@@ -11,6 +11,7 @@ from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
from vpp_object import VppObject
+from config import config
NUM_PKTS = 67
@@ -56,6 +57,7 @@ class VppL3xc(VppObject):
return "l3xc-%d" % self.intf.sw_if_index
+@unittest.skipIf("l3xc" in config.excluded_plugins, "Exclude L3XC plugin tests")
class TestL3xc(VppTestCase):
"""L3XC Test Case"""
diff --git a/test/test_lacp.py b/test/test_lacp.py
index af209501184..d8739775f27 100644
--- a/test/test_lacp.py
+++ b/test/test_lacp.py
@@ -10,12 +10,14 @@ from asfframework import VppTestRunner
from vpp_memif import VppSocketFilename, VppMemif
from vpp_bond_interface import VppBondInterface
from vpp_papi import VppEnum
+from config import config
bond_mac = "02:02:02:02:02:02"
lacp_dst_mac = "01:80:c2:00:00:02"
LACP_COLLECTION_AND_DISTRIBUTION_STATE = 63
+@unittest.skipIf("lacp" in config.excluded_plugins, "Exclude LACP plugin tests")
class TestMarker(VppTestCase):
"""LACP Marker Protocol Test Case"""
@@ -147,6 +149,7 @@ class TestMarker(VppTestCase):
bond1.remove_vpp_config()
+@unittest.skipIf("lacp" in config.excluded_plugins, "Exclude LACP plugin tests")
class TestLACP(VppTestCase):
"""LACP Test Case"""
diff --git a/test/test_lb.py b/test/test_lb.py
index e44684ecf2b..fc1674aeedf 100644
--- a/test/test_lb.py
+++ b/test/test_lb.py
@@ -11,6 +11,8 @@ from framework import VppTestCase
from util import ppp
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_ip import INVALID_INDEX
+from config import config
+import unittest
""" TestLB is a subclass of VPPTestCase classes.
@@ -32,6 +34,7 @@ from vpp_ip import INVALID_INDEX
"""
+@unittest.skipIf("lb" in config.excluded_plugins, "Exclude LB plugin tests")
class TestLB(VppTestCase):
"""Load Balancer Test Case"""
diff --git a/test/test_linux_cp.py b/test/test_linux_cp.py
index 95a9f1aca1f..ff6023cea26 100644
--- a/test/test_linux_cp.py
+++ b/test/test_linux_cp.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import unittest
+import socket
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6, Raw
@@ -20,6 +21,7 @@ from template_ipsec import (
IpsecTun4,
)
from test_ipsec_tun_if_esp import TemplateIpsecItf4
+from config import config
class VppLcpPair(VppObject):
@@ -51,6 +53,7 @@ class VppLcpPair(VppObject):
return False
+@unittest.skipIf("linux-cp" in config.excluded_plugins, "Exclude linux-cp plugin tests")
class TestLinuxCP(VppTestCase):
"""Linux Control Plane"""
@@ -239,7 +242,7 @@ class TestLinuxCP(VppTestCase):
self.assertEqual(rx[Ether].dst, phy.remote_mac)
self.assertEqual(rx[IP].dst, phy.remote_ip4)
self.assertEqual(rx[IP].src, phy.local_ip4)
- inner = IP(rx[IP].payload)
+ inner = IP(bytes(rx[IP].payload))
self.assertEqual(inner.src, tun4.local_ip4)
self.assertEqual(inner.dst, "2.2.2.2")
@@ -252,7 +255,7 @@ class TestLinuxCP(VppTestCase):
for rx in rxs:
self.assertEqual(rx[IPv6].dst, phy.remote_ip6)
self.assertEqual(rx[IPv6].src, phy.local_ip6)
- inner = IPv6(rx[IPv6].payload)
+ inner = IPv6(bytes(rx[IPv6].payload))
self.assertEqual(inner.src, tun6.local_ip6)
self.assertEqual(inner.dst, "2::2")
@@ -267,7 +270,7 @@ class TestLinuxCP(VppTestCase):
rxs = self.send_and_expect(phy, p * N_PKTS, self.pg4)
for rx in rxs:
- rx = IP(rx)
+ rx = IP(bytes(rx))
self.assertEqual(rx[IP].dst, tun4.local_ip4)
self.assertEqual(rx[IP].src, tun4.remote_ip4)
@@ -282,7 +285,7 @@ class TestLinuxCP(VppTestCase):
rxs = self.send_and_expect(phy, p * N_PKTS, self.pg5)
for rx in rxs:
- rx = IPv6(rx)
+ rx = IPv6(bytes(rx))
self.assertEqual(rx[IPv6].dst, tun6.local_ip6)
self.assertEqual(rx[IPv6].src, tun6.remote_ip6)
@@ -294,6 +297,7 @@ class TestLinuxCP(VppTestCase):
tun6.unconfig_ip6()
+@unittest.skipIf("linux-cp" in config.excluded_plugins, "Exclude linux-cp plugin tests")
class TestLinuxCPIpsec(TemplateIpsec, TemplateIpsecItf4, IpsecTun4):
"""IPsec Interface IPv4"""
@@ -350,7 +354,7 @@ class TestLinuxCPIpsec(TemplateIpsec, TemplateIpsecItf4, IpsecTun4):
def verify_decrypted(self, p, rxs):
for rx in rxs:
- rx = IP(rx)
+ rx = IP(bytes(rx))
self.assert_equal(rx[IP].src, p.tun_if.remote_ip4)
self.assert_equal(rx[IP].dst, p.tun_if.local_ip4)
self.assert_packet_checksums_valid(rx)
diff --git a/test/test_lisp.py b/test/test_lisp.py
index edc316ea7b2..7b6560eb3c2 100644
--- a/test/test_lisp.py
+++ b/test/test_lisp.py
@@ -19,6 +19,7 @@ from lisp import (
LispRemoteLocator,
)
from util import ppp
+from config import config
# From py_lispnetworking.lisp.py: # GNU General Public License v2.0
@@ -157,6 +158,7 @@ class SimpleDriver(Driver):
self.test.pg0.assert_nothing_captured()
+@unittest.skipIf("lisp" in config.excluded_plugins, "Exclude LISP plugin tests")
class TestLisp(VppTestCase):
"""Basic LISP test"""
@@ -206,6 +208,7 @@ class TestLisp(VppTestCase):
self.test_driver.run(self.deid_ip4)
+@unittest.skipIf("lisp" in config.excluded_plugins, "Exclude LISP plugin tests")
class TestLispUT(VppTestCase):
"""Lisp UT"""
diff --git a/test/test_lldp.py b/test/test_lldp.py
index 0a69be7c7b1..8b3f4cf1da2 100644
--- a/test/test_lldp.py
+++ b/test/test_lldp.py
@@ -96,6 +96,7 @@ class TestLldpCli(VppTestCase):
self.assertNotIn("pg0", reply)
+@unittest.skipIf("lldp" in config.excluded_plugins, "Exclude lldp plugin tests")
class TestLldpVapi(VppTestCase):
"""LLDP plugin test [VAPI]"""
diff --git a/test/test_map.py b/test/test_map.py
index c65c5df7cda..ffa9218b0c3 100644
--- a/test/test_map.py
+++ b/test/test_map.py
@@ -8,6 +8,7 @@ from asfframework import VppTestRunner
from vpp_ip import DpoProto
from vpp_ip_route import VppIpRoute, VppRoutePath
from util import fragment_rfc791, fragment_rfc8200
+from config import config
import scapy.compat
from scapy.layers.l2 import Ether
@@ -22,6 +23,7 @@ from scapy.layers.inet6 import (
)
+@unittest.skipIf("map" in config.excluded_plugins, "Exclude MAP plugin tests")
class TestMAP(VppTestCase):
"""MAP Test Case"""
diff --git a/test/test_map_br.py b/test/test_map_br.py
index a2c00d8d8ea..1f1bcccf792 100644
--- a/test/test_map_br.py
+++ b/test/test_map_br.py
@@ -5,6 +5,7 @@ import unittest
from framework import VppTestCase
from asfframework import VppTestRunner
from vpp_ip_route import VppIpRoute, VppRoutePath
+from config import config
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP, ICMP, TCP, IPerror, UDPerror
@@ -12,6 +13,7 @@ from scapy.layers.inet6 import IPv6, ICMPv6TimeExceeded, ICMPv6PacketTooBig
from scapy.layers.inet6 import ICMPv6EchoRequest, ICMPv6EchoReply, IPerror6
+@unittest.skipIf("map" in config.excluded_plugins, "Exclude MAP plugin tests")
class TestMAPBR(VppTestCase):
"""MAP-T Test Cases"""
diff --git a/test/test_memif.py b/test/test_memif.py
index 904343f2775..743c855b943 100644
--- a/test/test_memif.py
+++ b/test/test_memif.py
@@ -14,10 +14,12 @@ from remote_test import RemoteClass, RemoteVppTestCase
from vpp_memif import remove_all_memif_vpp_config, VppSocketFilename, VppMemif
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_papi import VppEnum
+from config import config
@tag_run_solo
@tag_fixme_debian11
+@unittest.skipIf("memif" in config.excluded_plugins, "Exclude Memif plugin tests")
class TestMemif(VppTestCase):
"""Memif Test Case"""
diff --git a/test/test_mpls.py b/test/test_mpls.py
index 0e747ec7cd0..daa0d967d2e 100644
--- a/test/test_mpls.py
+++ b/test/test_mpls.py
@@ -25,6 +25,7 @@ from vpp_ip_route import (
)
from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
from vpp_papi import VppEnum
+from config import config
import scapy.compat
from scapy.packet import Raw
@@ -1520,6 +1521,9 @@ class TestMPLS(VppTestCase):
rx = self.send_and_expect(self.pg1, tx, self.pg1)
self.verify_capture_ip6(self.pg0, rx, tx)
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_deag(self):
"""MPLS Deagg"""
diff --git a/test/test_mtu.py b/test/test_mtu.py
index 6735cc602a8..c5fb4278dfc 100644
--- a/test/test_mtu.py
+++ b/test/test_mtu.py
@@ -55,7 +55,7 @@ class TestMTU(VppTestCase):
i.admin_down()
def validate(self, rx, expected):
- self.assertEqual(rx, expected.__class__(expected))
+ self.assertTrue(bytes(rx), bytes(expected))
def validate_bytes(self, rx, expected):
self.assertEqual(rx, expected)
@@ -104,13 +104,10 @@ class TestMTU(VppTestCase):
/ p_ip4
/ p_payload
)
- n = icmp4_reply.__class__(icmp4_reply)
s = bytes(icmp4_reply)
icmp4_reply = s[0:576]
rx = self.send_and_expect_some(self.pg0, p4 * 11, self.pg0)
for p in rx:
- # p.show2()
- # n.show2()
self.validate_bytes(bytes(p[1]), icmp4_reply)
self.assert_error_counter_equal("/err/ip4-input/mtu_exceeded", 11)
@@ -185,7 +182,6 @@ class TestMTU(VppTestCase):
/ p_payload
)
icmp6_reply[2].hlim -= 1
- n = icmp6_reply.__class__(icmp6_reply)
s = bytes(icmp6_reply)
icmp6_reply_str = s[0:1280]
diff --git a/test/test_nat44_ed.py b/test/test_nat44_ed.py
index eed89f1a399..7615312ac31 100644
--- a/test/test_nat44_ed.py
+++ b/test/test_nat44_ed.py
@@ -21,8 +21,10 @@ from vpp_acl import AclRule, VppAcl, VppAclInterface
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_papi import VppEnum
from util import StatsDiff
+from config import config
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT44ED(VppTestCase):
"""NAT44ED Test Case"""
@@ -90,7 +92,7 @@ class TestNAT44ED(VppTestCase):
@classmethod
def create_and_add_ip4_table(cls, i, table_id=0):
- cls.vapi.ip_table_add_del(is_add=1, table={"table_id": table_id})
+ cls.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": table_id})
i.set_table_ip4(table_id)
@classmethod
@@ -162,8 +164,6 @@ class TestNAT44ED(VppTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
- if is_distro_ubuntu2204 == True and not hasattr(cls, "vpp"):
- return
cls.create_pg_interfaces(range(12))
cls.interfaces = list(cls.pg_interfaces[:4])
@@ -174,7 +174,7 @@ class TestNAT44ED(VppTestCase):
cls.configure_ip4_interface(i, hosts=3)
# test specific (test-multiple-vrf)
- cls.vapi.ip_table_add_del(is_add=1, table={"table_id": 1})
+ cls.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": 1})
# test specific (test-one-armed-nat44-static)
cls.pg4.generate_remote_hosts(2)
@@ -2607,6 +2607,7 @@ class TestNAT44ED(VppTestCase):
@tag_fixme_ubuntu2204
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT44EDMW(TestNAT44ED):
"""NAT44ED MW Test Case"""
@@ -4399,8 +4400,8 @@ class TestNAT44EDMW(TestNAT44ED):
self.pg7.unconfig()
self.pg8.unconfig()
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id_in})
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id_out})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": vrf_id_in})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": vrf_id_out})
def test_dynamic_output_feature_vrf(self):
"""NAT44ED dynamic translation test: output-feature, VRF"""
@@ -4469,7 +4470,7 @@ class TestNAT44EDMW(TestNAT44ED):
self.pg7.unconfig()
self.pg8.unconfig()
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": new_vrf_id})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": new_vrf_id})
def test_next_src_nat(self):
"""NAT44ED On way back forward packet to nat44-in2out node."""
diff --git a/test/test_nat44_ed_output.py b/test/test_nat44_ed_output.py
index dbf1dc40a75..55c43196295 100644
--- a/test/test_nat44_ed_output.py
+++ b/test/test_nat44_ed_output.py
@@ -3,12 +3,15 @@
import random
import unittest
+import struct
+import socket
from scapy.layers.inet import Ether, IP, TCP
from scapy.packet import Raw
from scapy.data import IP_PROTOS
from framework import VppTestCase
from asfframework import VppTestRunner
from vpp_papi import VppEnum
+from config import config
def get_nat44_ed_in2out_worker_index(ip, vpp_worker_count):
@@ -21,6 +24,7 @@ def get_nat44_ed_in2out_worker_index(ip, vpp_worker_count):
return 1 + h % vpp_worker_count
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT44EDOutput(VppTestCase):
"""NAT44 ED output feature Test Case"""
diff --git a/test/test_nat44_ei.py b/test/test_nat44_ei.py
index ae9194b87ce..166044db435 100644
--- a/test/test_nat44_ei.py
+++ b/test/test_nat44_ei.py
@@ -36,6 +36,7 @@ from util import ppp
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_neighbor import VppNeighbor
from vpp_papi import VppEnum
+from config import config
# NAT HA protocol event data
@@ -913,6 +914,7 @@ def get_nat44_ei_in2out_worker_index(ip, vpp_worker_count):
@tag_fixme_debian11
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT44EI(MethodHolder):
"""NAT44EI Test Cases"""
@@ -953,8 +955,8 @@ class TestNAT44EI(MethodHolder):
cls.pg1.configure_ipv4_neighbors()
cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7]))
- cls.vapi.ip_table_add_del(is_add=1, table={"table_id": 10})
- cls.vapi.ip_table_add_del(is_add=1, table={"table_id": 20})
+ cls.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": 10})
+ cls.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": 20})
cls.pg4._local_ip4 = "172.16.255.1"
cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2"
@@ -2682,8 +2684,8 @@ class TestNAT44EI(MethodHolder):
self.pg0.unconfig_ip4()
self.pg1.unconfig_ip4()
- self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id1})
- self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id2})
+ self.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": vrf_id1})
+ self.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": vrf_id2})
self.pg0.set_table_ip4(vrf_id1)
self.pg1.set_table_ip4(vrf_id2)
self.pg0.config_ip4()
@@ -2730,8 +2732,8 @@ class TestNAT44EI(MethodHolder):
self.pg1.config_ip4()
self.pg0.resolve_arp()
self.pg1.resolve_arp()
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id1})
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id2})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": vrf_id1})
+ self.vapi.ip_table_add_del_v2(is_add=0, table={"table_id": vrf_id2})
def test_vrf_feature_independent(self):
"""NAT44EI tenant VRF independent address pool mode"""
@@ -3468,8 +3470,8 @@ class TestNAT44EI(MethodHolder):
self.pg1.unconfig_ip4()
self.pg2.unconfig_ip4()
- self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id1})
- self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id2})
+ self.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": vrf_id1})
+ self.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": vrf_id2})
self.pg1.set_table_ip4(vrf_id1)
self.pg2.set_table_ip4(vrf_id2)
self.pg1.config_ip4()
@@ -4122,6 +4124,7 @@ class TestNAT44EI(MethodHolder):
i.remove_vpp_config()
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT44Out2InDPO(MethodHolder):
"""NAT44EI Test Cases using out2in DPO"""
@@ -4263,6 +4266,7 @@ class TestNAT44Out2InDPO(MethodHolder):
self.verify_capture_in(capture, self.pg0)
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT44EIMW(MethodHolder):
"""NAT44EI Test Cases (multiple workers)"""
@@ -4302,8 +4306,8 @@ class TestNAT44EIMW(MethodHolder):
cls.pg1.configure_ipv4_neighbors()
cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7]))
- cls.vapi.ip_table_add_del(is_add=1, table={"table_id": 10})
- cls.vapi.ip_table_add_del(is_add=1, table={"table_id": 20})
+ cls.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": 10})
+ cls.vapi.ip_table_add_del_v2(is_add=1, table={"table_id": 20})
cls.pg4._local_ip4 = "172.16.255.1"
cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2"
diff --git a/test/test_nat64.py b/test/test_nat64.py
index f650b8d5f43..a28d7257aa0 100644
--- a/test/test_nat64.py
+++ b/test/test_nat64.py
@@ -36,10 +36,12 @@ from syslog_rfc5424_parser import SyslogMessage, ParseError
from syslog_rfc5424_parser.constants import SyslogSeverity
from util import ppc, ppp
from vpp_papi import VppEnum
+from config import config
@tag_fixme_vpp_workers
@tag_fixme_ubuntu2204
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT64(VppTestCase):
"""NAT64 Test Cases"""
@@ -55,8 +57,6 @@ class TestNAT64(VppTestCase):
def setUpClass(cls):
super(TestNAT64, cls).setUpClass()
- if is_distro_ubuntu2204 == True and not hasattr(cls, "vpp"):
- return
cls.tcp_port_in = 6303
cls.tcp_port_out = 6303
cls.udp_port_in = 6304
@@ -76,7 +76,7 @@ class TestNAT64(VppTestCase):
cls.ip6_interfaces.append(cls.pg_interfaces[2])
cls.ip4_interfaces = list(cls.pg_interfaces[1:2])
- cls.vapi.ip_table_add_del(
+ cls.vapi.ip_table_add_del_v2(
is_add=1, table={"table_id": cls.vrf1_id, "is_ip6": 1}
)
diff --git a/test/test_nat66.py b/test/test_nat66.py
index 44df7222f9d..54f9321d44e 100644
--- a/test/test_nat66.py
+++ b/test/test_nat66.py
@@ -13,8 +13,10 @@ from scapy.layers.inet6 import (
from scapy.layers.l2 import Ether, GRE
from util import ppp
from vpp_papi import VppEnum
+from config import config
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestNAT66(VppTestCase):
"""NAT66 Test Cases"""
diff --git a/test/test_npt66.py b/test/test_npt66.py
index 307dbabc39b..7479d3029b8 100644
--- a/test/test_npt66.py
+++ b/test/test_npt66.py
@@ -4,12 +4,14 @@ import unittest
import ipaddress
from framework import VppTestCase
from asfframework import VppTestRunner
+from config import config
from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6DestUnreach
from scapy.layers.l2 import Ether
from scapy.packet import Raw
+@unittest.skipIf("npt66" in config.excluded_plugins, "Exclude NPTv6 plugin tests")
class TestNPT66(VppTestCase):
"""NPTv6 Test Case"""
diff --git a/test/test_pcap.py b/test/test_pcap.py
index 72d215cea06..b73a601bcc8 100644
--- a/test/test_pcap.py
+++ b/test/test_pcap.py
@@ -9,8 +9,12 @@ from scapy.packet import Raw
from framework import VppTestCase
from asfframework import VppTestRunner
+from config import config
+@unittest.skipIf(
+ "dispatch-trace" in config.excluded_plugins, "Exclude dispatch trace plugin tests"
+)
class TestPcap(VppTestCase):
"""Pcap Unit Test Cases"""
diff --git a/test/test_pg.py b/test/test_pg.py
index d2e656df58b..da3b2254968 100644
--- a/test/test_pg.py
+++ b/test/test_pg.py
@@ -83,7 +83,7 @@ class TestPgTun(VppTestCase):
rxs = self.send_and_expect(self.pg0, p * N_PKTS, self.pg1)
for rx in rxs:
- rx = IP(rx)
+ rx = IP(bytes(rx))
self.assertFalse(rx.haslayer(Ether))
self.assertEqual(rx[IP].dst, self.pg1.remote_ip4)
@@ -97,7 +97,7 @@ class TestPgTun(VppTestCase):
rxs = self.send_and_expect(self.pg0, p * N_PKTS, self.pg2)
for rx in rxs:
- rx = IPv6(rx)
+ rx = IPv6(bytes(rx))
self.assertFalse(rx.haslayer(Ether))
self.assertEqual(rx[IPv6].dst, self.pg2.remote_ip6)
diff --git a/test/test_ping.py b/test/test_ping.py
index f3da7eb96ca..654d47be161 100644
--- a/test/test_ping.py
+++ b/test/test_ping.py
@@ -3,6 +3,9 @@ from scapy.layers.inet import IP, ICMP
from framework import VppTestCase
from vpp_ip_route import VppIpInterfaceAddress, VppIpRoute, VppRoutePath
from vpp_neighbor import VppNeighbor
+from config import config
+
+import unittest
""" TestPing is a subclass of VPPTestCase classes.
@@ -11,6 +14,7 @@ Basic test for sanity check of the ping.
"""
+@unittest.skipIf("ping" in config.excluded_plugins, "Exclude Ping plugin tests")
class TestPing(VppTestCase):
"""Ping Test Case"""
diff --git a/test/test_pipe.py b/test/test_pipe.py
index 83f5f96c998..01d2f30f494 100644
--- a/test/test_pipe.py
+++ b/test/test_pipe.py
@@ -11,6 +11,7 @@ from asfframework import VppTestRunner
from vpp_interface import VppInterface
from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
from vpp_acl import AclRule, VppAcl, VppAclInterface
+from config import config
NUM_PKTS = 67
@@ -63,6 +64,7 @@ class VppPipe(VppInterface):
)
+@unittest.skipIf("acl" in config.excluded_plugins, "Exclude tests requiring ACL plugin")
class TestPipe(VppTestCase):
"""Pipes"""
diff --git a/test/test_pnat.py b/test/test_pnat.py
index a7bd24b612c..ef5b3a107dc 100644
--- a/test/test_pnat.py
+++ b/test/test_pnat.py
@@ -6,8 +6,10 @@ from scapy.layers.inet import Ether, IP, UDP, ICMP
from framework import VppTestCase
from asfframework import VppTestRunner
from vpp_papi import VppEnum
+from config import config
+@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
class TestPNAT(VppTestCase):
"""PNAT Test Case"""
@@ -38,7 +40,7 @@ class TestPNAT(VppTestCase):
i.admin_down()
def validate(self, rx, expected):
- self.assertEqual(rx, expected.__class__(expected))
+ self.assertTrue(bytes(rx), bytes(expected))
def validate_bytes(self, rx, expected):
self.assertEqual(rx, expected)
diff --git a/test/test_pppoe.py b/test/test_pppoe.py
index e396250621f..b0ea5cfe4f4 100644
--- a/test/test_pppoe.py
+++ b/test/test_pppoe.py
@@ -13,8 +13,10 @@ from asfframework import VppTestRunner
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_pppoe_interface import VppPppoeInterface
from util import ppp
+from config import config
+@unittest.skipIf("pppoe" in config.excluded_plugins, "Exclude PPPoE plugin tests")
class TestPPPoE(VppTestCase):
"""PPPoE Test Case"""
@@ -222,7 +224,6 @@ class TestPPPoE(VppTestCase):
)
pppoe_if.add_vpp_config()
pppoe_if.set_unnumbered(self.pg0.sw_if_index)
-
#
# Send tunneled packets that match the created tunnel and
# are decapped and forwarded
diff --git a/test/test_pvti.py b/test/test_pvti.py
new file mode 100644
index 00000000000..94ae7790323
--- /dev/null
+++ b/test/test_pvti.py
@@ -0,0 +1,1153 @@
+#!/usr/bin/env python3
+""" PVTI tests """
+
+import datetime
+import base64
+import os
+import copy
+import struct
+
+from hashlib import blake2s
+from config import config
+from scapy.packet import Raw
+from scapy.compat import raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IPv6
+from scapy.layers.vxlan import VXLAN
+
+from vpp_interface import VppInterface
+from vpp_pg_interface import is_ipv6_misc
+from vpp_ip_route import VppIpRoute, VppRoutePath
+from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
+from vpp_vxlan_tunnel import VppVxlanTunnel
+from vpp_object import VppObject
+from vpp_papi import VppEnum
+from asfframework import tag_run_solo, tag_fixme_vpp_debug
+from framework import VppTestCase
+from re import compile
+import unittest
+
+
+from scapy.packet import Packet, bind_layers
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IPv6
+from scapy.fields import (
+ FlagsField,
+ XByteField,
+ XShortField,
+ ThreeBytesField,
+ ConditionalField,
+ ShortField,
+ ByteEnumField,
+ X3BytesField,
+ LEIntField,
+ ByteField,
+ StrLenField,
+ PacketListField,
+ LEShortField,
+ IntField,
+ ShortField,
+ XIntField,
+)
+
+import sys
+
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
+
+
+#
+# A custom decoder for Scapy for PVTI packet format
+#
+
+
+class PVTIChunk(Packet):
+ name = "PVTIChunk"
+ fields_desc = [
+ ShortField("total_chunk_length", None),
+ XShortField("_pad0", 0),
+ XIntField("_pad1", 0),
+ StrLenField("data", "", length_from=lambda pkt: pkt.total_chunk_length - 8),
+ ]
+
+ # This prevents the first chunk from consuming the entire remaining
+ # contents of the packet
+ def extract_padding(self, s):
+ return "", s
+
+ def post_build(self, p, pay):
+ if self.total_chunk_length is None and self.data:
+ chunk_header_size = 8
+ l = chunk_header_size + len(self.data)
+ p = struct.pack("!H", l) + p[2:]
+ return p + pay
+
+
+class PVTI(Packet):
+ name = "PVTI"
+ PVTI_ALIGN_BYTES = 9
+ fields_desc = [
+ IntField("seq", 0x0),
+ ByteField("stream_index", 0),
+ ByteField("chunk_count", None),
+ ByteField("reass_chunk_count", 0),
+ ByteField("mandatory_flags_mask", 0),
+ ByteField("flags_value", 0),
+ ByteField("pad_bytes", PVTI_ALIGN_BYTES),
+ StrLenField(
+ "pad", b"\xca" * PVTI_ALIGN_BYTES, length_from=lambda pkt: pkt.pad_bytes
+ ),
+ PacketListField("chunks", [], PVTIChunk, count_from=lambda p: p.chunk_count),
+ ]
+
+ def mysummary(self):
+ return self.sprintf("PVTI (len=%PVTI.total_len%)")
+
+ def post_build(self, p, pay):
+ if self.chunk_count is None:
+ l = len(self.chunks)
+ # offset of the chunk count within the fields
+ offset_of_chunk_count = 5
+ p = (
+ p[:offset_of_chunk_count]
+ + struct.pack("b", l)
+ + p[offset_of_chunk_count + 1 :]
+ )
+ return p + pay
+
+
+bind_layers(UDP, PVTI, dport=12312)
+# By default, set both ports to the test
+# bind_layers(UDP, PVTI, sport=6192, dport=6192)
+
+
+# PVTI ENcapsulator/DEcapsulator
+class PvtiEnDe(object):
+ """
+ PVTI encapsulator/decapsulator
+ """
+
+ def __init__(
+ self,
+ local_ip,
+ local_port,
+ remote_ip,
+ remote_port,
+ underlay_mtu=1500,
+ for_rx_test=False,
+ ):
+ self.for_rx_test = for_rx_test
+ self.local_ip = local_ip
+ self.local_port = local_port
+ self.remote_ip = remote_ip
+ self.remote_port = remote_port
+ self.underlay_mtu = underlay_mtu
+ self.stream_index = 0
+ self.tx_chunks = []
+ self.tx_n_reass_chunks = 0
+ self.tx_seq = 42
+ # payload = chunk headers + data
+ self.max_payload_len = underlay_mtu - len(raw(IP() / UDP() / PVTI()))
+ self.pvti_header_len = len(raw(PVTI()))
+ self.chunk_header_len = len(raw(PVTIChunk()))
+
+ def get_curr_payload_len(self):
+ tx_len = 0
+ for c in self.tx_chunks:
+ tx_len = tx_len + len(c.data) + self.chunk_header_len
+ return tx_len
+
+ def get_payload_room(self):
+ return self.max_payload_len - self.get_curr_payload_len()
+
+ def flush_tx_chunks(self, more_frags=False):
+ if self.for_rx_test:
+ ip_dst = self.local_ip
+ ip_src = self.remote_ip
+ else:
+ ip_src = self.local_ip
+ ip_dst = self.remote_ip
+ p = (
+ IP(
+ src=ip_src,
+ dst=ip_dst,
+ ttl=127,
+ frag=0,
+ flags=0,
+ id=self.tx_seq,
+ )
+ / UDP(sport=self.local_port, dport=self.remote_port, chksum=0)
+ / PVTI(
+ reass_chunk_count=self.tx_n_reass_chunks,
+ seq=self.tx_seq,
+ stream_index=self.stream_index,
+ chunks=self.tx_chunks,
+ )
+ )
+
+ p = IP(raw(p))
+
+ self.tx_n_reass_chunks = 0
+ self.tx_chunks = []
+ self.tx_seq = self.tx_seq + 1
+ return p
+
+ def encap_pkt(self, p):
+ out = []
+ if IP in p:
+ p[IP].ttl = p[IP].ttl - 1
+ payload_wip = p[IP].build()
+ elif IPv6 in p:
+ p[IPv6].hlim = p[IPv6].hlim - 1
+ payload_wip = p[IPv6].build()
+
+ split_chunks = False
+ huge_solo_packet = (
+ len(payload_wip) + self.chunk_header_len > self.get_payload_room()
+ ) and len(self.tx_chunks) == 0
+
+ while True:
+ available_room = self.get_payload_room()
+ chunk_wip_len = len(payload_wip) + self.chunk_header_len
+ xpad0 = 0xABAB
+ xpad1 = 0xABABABAB
+
+ if chunk_wip_len <= available_room:
+ # happy case - there is enough space to fit the entire chunk
+ if split_chunks:
+ self.tx_n_reass_chunks = self.tx_n_reass_chunks + 1
+ tx = PVTIChunk(data=payload_wip, _pad0=xpad0, _pad1=xpad1)
+ self.tx_chunks.append(tx)
+ if chunk_wip_len == available_room:
+ # an unlikely perfect fit - send this packet.
+ out.append(self.flush_tx_chunks())
+ break
+ elif available_room < self.chunk_header_len + 1:
+ # Can not fit even a chunk header + 1 byte of data
+ # Flush and retry
+ out.append(self.flush_tx_chunks())
+ continue
+ else:
+ # Chop as much as we can from the packet
+ chop_len = available_room - self.chunk_header_len
+ if split_chunks:
+ self.tx_n_reass_chunks = self.tx_n_reass_chunks + 1
+ tx = PVTIChunk(data=payload_wip[:chop_len], _pad0=xpad0, _pad1=xpad1)
+ self.tx_chunks.append(tx)
+ out.append(self.flush_tx_chunks())
+ split_chunks = True
+ payload_wip = payload_wip[chop_len:]
+ continue
+ return out
+
+ def encap_packets(self, pkts):
+ out = []
+ self.start_encap()
+ for p in pkts:
+ out.extend(self.encap_pkt(p))
+ last_pkt = self.finish_encap()
+ if last_pkt != None:
+ out.append(last_pkt)
+ return out
+
+ def start_encap(self):
+ return None
+
+ def finish_encap(self):
+ out = None
+ if len(self.tx_chunks) > 0:
+ out = self.flush_tx_chunks()
+ return out
+
+
+""" TestPvti is a subclass of VPPTestCase classes.
+
+PVTI test.
+
+"""
+
+
+def get_field_bytes(pkt, name):
+ fld, val = pkt.getfield_and_val(name)
+ return fld.i2m(pkt, val)
+
+
+class VppPvtiInterface(VppInterface):
+ """
+ VPP PVTI interface
+ """
+
+ def __init__(
+ self, test, local_ip, local_port, remote_ip, remote_port, underlay_mtu=1500
+ ):
+ super(VppPvtiInterface, self).__init__(test)
+
+ self.local_ip = local_ip
+ self.local_port = local_port
+ self.remote_ip = remote_ip
+ self.remote_port = remote_port
+ self.underlay_mtu = underlay_mtu
+
+ def get_ende(self, for_rx_test=False):
+ return PvtiEnDe(
+ self.local_ip,
+ self.local_port,
+ self.remote_ip,
+ self.remote_port,
+ self.underlay_mtu,
+ for_rx_test,
+ )
+
+ def verify_encap_packets(self, orig_pkts, recv_pkts):
+ ende = self.get_ende()
+ recv2_pkts = ende.encap_packets(orig_pkts)
+ out1 = []
+ out2 = []
+ for i, pkt in enumerate(recv_pkts):
+ if IP in pkt:
+ rx_pkt = pkt[IP]
+ elif IPv6 in pkt:
+ rx_pkt = pkt[IPv6]
+ else:
+ raise "Neither IPv4 nor IPv6"
+ py_pkt = recv2_pkts[i]
+ if rx_pkt != py_pkt:
+ eprint("received packet:")
+ rx_pkt.show()
+ eprint("python packet:")
+ py_pkt.show()
+ out1.append(rx_pkt)
+ out2.append(py_pkt)
+ return (out1, out2)
+
+ def add_vpp_config(self):
+ r = self.test.vapi.pvti_interface_create(
+ interface={
+ "local_ip": self.local_ip,
+ "local_port": self.local_port,
+ "remote_ip": self.remote_ip,
+ "remote_port": self.remote_port,
+ "underlay_mtu": self.underlay_mtu,
+ }
+ )
+ self.set_sw_if_index(r.sw_if_index)
+ self.test.registry.register(self, self.test.logger)
+ return self
+
+ def remove_vpp_config(self):
+ self.test.vapi.pvti_interface_delete(sw_if_index=self._sw_if_index)
+
+ def query_vpp_config(self):
+ ts = self.test.vapi.pvti_interface_dump(sw_if_index=0xFFFFFFFF)
+ for t in ts:
+ if (
+ t.interface.sw_if_index == self._sw_if_index
+ and str(t.interface.local_ip) == self.local_ip
+ and t.interface.local_port == self.local_port
+ and t.interface.remote_port == self.remote_port
+ and str(t.interface.remote_ip) == self.remote_ip
+ ):
+ self.test.logger.info("QUERY AYXX: true")
+ return True
+ return False
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "pvti-%d" % self._sw_if_index
+
+
+@unittest.skipIf("pvti" in config.excluded_plugins, "Exclude PVTI plugin tests")
+# @tag_run_solo
+class TestPvti(VppTestCase):
+ """Packet Vector Tunnel Interface (PVTI) Test Case"""
+
+ error_str = compile(r"Error")
+
+ # maxDiff = None
+
+ wg4_output_node_name = "/err/wg4-output-tun/"
+ wg4_input_node_name = "/err/wg4-input/"
+ wg6_output_node_name = "/err/wg6-output-tun/"
+ wg6_input_node_name = "/err/wg6-input/"
+ kp4_error = wg4_output_node_name + "Keypair error"
+ mac4_error = wg4_input_node_name + "Invalid MAC handshake"
+ peer4_in_err = wg4_input_node_name + "Peer error"
+ peer4_out_err = wg4_output_node_name + "Peer error"
+ kp6_error = wg6_output_node_name + "Keypair error"
+ mac6_error = wg6_input_node_name + "Invalid MAC handshake"
+ peer6_in_err = wg6_input_node_name + "Peer error"
+ peer6_out_err = wg6_output_node_name + "Peer error"
+ cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
+ cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
+ ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
+ ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestPvti, cls).setUpClass()
+ try:
+ cls.create_pg_interfaces(range(2))
+ for i in cls.pg_interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.config_ip6()
+ i.resolve_arp()
+ i.resolve_ndp()
+
+ except Exception:
+ super(TestPvti, cls).tearDownClass()
+ raise
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestPvti, cls).tearDownClass()
+
+ def setUp(self):
+ super(VppTestCase, self).setUp()
+ self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
+ self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
+ self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
+ self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
+ self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
+ self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
+ self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
+ self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
+ self.base_cookie_dec4_err = self.statistics.get_err_counter(
+ self.cookie_dec4_err
+ )
+ self.base_cookie_dec6_err = self.statistics.get_err_counter(
+ self.cookie_dec6_err
+ )
+ self.base_ratelimited4_err = self.statistics.get_err_counter(
+ self.ratelimited4_err
+ )
+ self.base_ratelimited6_err = self.statistics.get_err_counter(
+ self.ratelimited6_err
+ )
+
+ def create_packets(
+ self, src_ip_if, count=1, size=150, for_rx=False, is_ip6=False, af_mix=False
+ ):
+ pkts = []
+ total_packet_count = count
+ padstr0 = ""
+ padstr1 = ""
+ for i in range(0, 2000):
+ padstr0 = padstr0 + (".%03x" % i)
+ padstr1 = padstr1 + ("+%03x" % i)
+
+ for i in range(0, total_packet_count):
+ if af_mix:
+ is_ip6 = i % 2 == 1
+
+ dst_mac = src_ip_if.local_mac
+ src_mac = src_ip_if.remote_mac
+ if for_rx:
+ dst_ip4 = src_ip_if.remote_ip4
+ dst_ip6 = src_ip_if.remote_ip6
+ src_ip4 = "10.0.%d.4" % i
+ src_ip6 = "2001:db8::%x" % i
+ else:
+ src_ip4 = src_ip_if.remote_ip4
+ src_ip6 = src_ip_if.remote_ip6
+ dst_ip4 = "10.0.%d.4" % i
+ dst_ip6 = "2001:db8::%x" % i
+ src_l4 = 1234 + i
+ dst_l4 = 4321 + i
+
+ ulp = UDP(sport=src_l4, dport=dst_l4)
+ payload = "test pkt #%d" % i
+ if i % 2 == 1:
+ padstr = padstr1
+ else:
+ padstr = padstr0
+
+ p = Ether(dst=dst_mac, src=src_mac)
+ if is_ip6:
+ p /= IPv6(src=src_ip6, dst=dst_ip6)
+ else:
+ p /= IP(src=src_ip4, dst=dst_ip4, frag=0, flags=0)
+
+ p /= ulp / Raw(payload)
+
+ if i % 2 == 1 or total_packet_count == 1:
+ self.extend_packet(p, size, padstr)
+ else:
+ self.extend_packet(p, 150, padstr)
+ pkts.append(p)
+ return pkts
+
+ def add_rx_ether_header(self, in_pkts, rx_intf=None):
+ out = []
+ if rx_intf is None:
+ rx_intf = self.pg0
+ dst_mac = rx_intf.local_mac
+ src_mac = rx_intf.remote_mac
+ pkts = []
+ for p in in_pkts:
+ p0 = Ether(dst=dst_mac, src=src_mac) / p[IP]
+ out.append(p0)
+ return out
+
+ def encap_for_rx_test(self, pkts, rx_intf=None):
+ ende = self.pvti0.get_ende(for_rx_test=True)
+ encap_pkts = ende.encap_packets(pkts)
+ return self.add_rx_ether_header(encap_pkts, rx_intf)
+
+ def decrement_ttl_and_build(self, send_pkts):
+ out = []
+ pkts = copy.deepcopy(send_pkts)
+ for p in pkts:
+ p[IP].ttl = p[IP].ttl - 1
+ out.append(Ether(p.build()))
+ return out
+
+ def create_rx_packets(self, dst_ip_if, rx_intf=None, count=1, size=150):
+ pkts = []
+ total_packet_count = count
+ padstr = ""
+ if rx_intf is None:
+ rx_intf = self.pg0
+ for i in range(0, 2000):
+ padstr = padstr + (".%03x" % i)
+
+ dst_mac = rx_intf.local_mac
+ src_mac = rx_intf.remote_mac
+
+ for i in range(0, total_packet_count):
+ dst_ip4 = dst_ip_if.remote_ip4
+ src_ip4 = "10.0.%d.4" % i
+ src_l4 = 1234 + i
+ dst_l4 = 4321 + i
+
+ ulp = UDP(sport=src_l4, dport=dst_l4)
+ payload = "test"
+
+ # if i % 2 == 1 or total_packet_count == 1:
+ # self.extend_packet(p, size, padstr)
+ # else:
+ # self.extend_packet(p, 150, padstr)
+
+ pvti = PVTI(seq=42 + i, chunks=[])
+ for j in range(0, 32):
+ p = (
+ IP(src=src_ip4, dst=dst_ip4, frag=0, flags=0, id=j + 0x4000)
+ / ulp
+ / Raw(payload)
+ )
+ chunk0 = PVTIChunk(data=raw(p))
+ pvti.chunks.append(chunk0)
+
+ p = (
+ Ether(dst=dst_mac, src=src_mac)
+ / IP(src="192.0.2.1", dst=rx_intf.local_ip4, id=0x3000 + i)
+ / UDP(sport=12312, dport=12312)
+ / pvti
+ )
+ # p.show()
+ # Ether(raw(p)).show()
+
+ pkts.append(p)
+ return pkts
+
+ def send_and_assert_no_replies_ignoring_init(
+ self, intf, pkts, remark="", timeout=None
+ ):
+ self.pg_send(intf, pkts)
+
+ def _filter_out_fn(p):
+ return is_ipv6_misc(p) or is_handshake_init(p)
+
+ try:
+ if not timeout:
+ timeout = 1
+ for i in self.pg_interfaces:
+ i.assert_nothing_captured(
+ timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
+ )
+ timeout = 0.1
+ finally:
+ pass
+
+ def test_0000_pvti_interface(self):
+ """Simple interface creation"""
+ local_port = 12312
+ peer_addr = self.pg0.remote_ip4 # "192.0.2.1"
+ peer_port = 31234
+ peer_port = 12312
+
+ # Create interface
+ pvti0 = VppPvtiInterface(
+ self, self.pg1.local_ip4, local_port, peer_addr, peer_port
+ ).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show pvti tx peers"))
+ self.logger.info(self.vapi.cli("show pvti rx peers"))
+
+ # delete interface
+ pvti0.remove_vpp_config()
+ # self.logger.info(self.vapi.cli("show pvti interface"))
+ # pvti0.add_vpp_config()
+
+ def test_0001_pvti_send_simple_1pkt(self):
+ """v4o4 TX: Simple packet: 1 -> 1"""
+
+ self.prepare_for_test("v4o4_1pkt_simple")
+ pkts = self.create_packets(self.pg1)
+
+ recv_pkts = self.send_and_expect(self.pg1, pkts, self.pg0)
+ for p in recv_pkts:
+ self.logger.info(p)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(pkts, recv_pkts)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0101_pvti_send_simple_1pkt(self):
+ """v6o4 TX: Simple packet: 1 -> 1"""
+
+ self.prepare_for_test("v6o4_1pkt_simple")
+ pkts = self.create_packets(self.pg1, is_ip6=True)
+
+ recv_pkts = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=1)
+ for p in recv_pkts:
+ self.logger.info(p)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(pkts, recv_pkts)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0002_pvti_send_simple_2pkt(self):
+ """TX: Simple packet: 2 -> 1"""
+ self.prepare_for_test("2pkt_simple")
+
+ send_pkts = self.create_packets(self.pg1, count=2)
+ pkts = copy.deepcopy(send_pkts)
+ rx = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=1)
+ for p in rx:
+ self.logger.info(p)
+ # p.show()
+
+ payload0 = rx[0][PVTI].chunks[0].data
+ payload1 = rx[0][PVTI].chunks[1].data
+
+ pktA0 = IP(payload0)
+ pktA1 = IP(payload1)
+
+ p0 = pkts[0][IP]
+ p0.ttl = p0.ttl - 1
+ pktB0 = IP(p0.build())
+
+ p1 = pkts[1][IP]
+ p1.ttl = p1.ttl - 1
+ pktB1 = IP(p1.build())
+
+ self.assertEqual(pktA0, pktB0)
+ self.assertEqual(pktA1, pktB1)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def prepare_for_test(self, test_name, underlay_mtu=1500, is_ip6=False):
+ local_port = 12312
+ peer_ip4_addr = "192.0.2.1"
+ peer_ip6_addr = "2001:db8:dead::1"
+ peer_port = 31234
+ peer_port = 12312
+ for i in self.pg_interfaces:
+ i.test_name = test_name
+ if is_ip6:
+ self.pvti0 = VppPvtiInterface(
+ self,
+ self.pg1.local_ip6,
+ local_port,
+ peer_ip6_addr,
+ peer_port,
+ underlay_mtu,
+ ).add_vpp_config()
+ else:
+ self.pvti0 = VppPvtiInterface(
+ self,
+ self.pg1.local_ip4,
+ local_port,
+ peer_ip4_addr,
+ peer_port,
+ underlay_mtu,
+ ).add_vpp_config()
+ self.pvti0.config_ip4()
+ self.pvti0.config_ip6()
+ self.pvti0.admin_up()
+
+ self.logger.info(self.vapi.cli("ip route add 0.0.0.0/0 via 172.16.3.3"))
+ ## FIXME: using direct "interface" below results in blackouts. intermittently.
+ # self.logger.info(self.vapi.cli("ip route 0.0.0.0/0 via pvti0"))
+ self.logger.info(self.vapi.cli("ip route add ::/0 via pvti0"))
+ self.logger.info(self.vapi.cli("ip route add 192.0.2.1/32 via pg0"))
+ self.logger.info(self.vapi.cli("ip neighbor pg0 192.0.2.1 000c.0102.0304"))
+ self.logger.info(self.vapi.cli("ip route 2001:db8:dead::1/128 via pg0"))
+ self.logger.info(
+ self.vapi.cli("ip neighbor pg0 2001:db8:dead::1 000c.0102.0304")
+ )
+ self.logger.info(self.vapi.cli("ip neighbor pg1 172.16.2.2 000c.0102.0304"))
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("sh ip fib"))
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("set interface ip pvti-bypass pg0"))
+
+ def cleanup_after_test(self):
+ self.logger.info(self.vapi.cli("ip neighbor del pg0 192.0.2.1 000c.0102.0304"))
+ self.logger.info(self.vapi.cli("ip neighbor del pg1 172.16.2.2 000c.0102.0304"))
+ self.logger.info(self.vapi.cli("ip route del 192.0.2.1/32 via pg0"))
+ # self.logger.info(self.vapi.cli("ip route del 0.0.0.0/0 via pvti0"))
+ self.logger.info(self.vapi.cli("ip route del ::/0 via pvti0"))
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.pvti0.remove_vpp_config()
+
+ def test_0003_pvti_send_simple_1pkt_big(self):
+ """TX: Simple big packet: 1 -> 2"""
+ self.prepare_for_test("1big_pkt")
+
+ send_pkts = self.create_packets(self.pg1, count=1, size=1900)
+ pkts = copy.deepcopy(send_pkts)
+ self.logger.info("count: ")
+ self.logger.info(len(pkts))
+ rx = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=2)
+ for p in rx:
+ self.logger.info(p)
+ self.logger.info(len(p[PVTI].chunks[0].data))
+ # p.show()
+ payload = rx[0][PVTI].chunks[0].data + rx[1][PVTI].chunks[0].data
+
+ pkt1 = IP(payload)
+ p0 = pkts[0][IP]
+ p0.ttl = p0.ttl - 1
+
+ pkt0 = IP(p0.build())
+
+ self.assertEqual(pkt0, pkt1)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0004_pvti_send_simple_5pkt_big(self):
+ """v4o4 TX: Simple big packets: 5 -> 2"""
+ self.prepare_for_test("v4o4_5big_pkt")
+
+ send_pkts = self.create_packets(self.pg1, count=5, size=1050)
+ self.logger.info("count: %d " % len(send_pkts))
+ # self.logger.info(len(pkts))
+ rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=2)
+ for p in rx:
+ self.logger.info(p)
+ self.logger.info(len(p[PVTI].chunks[0].data))
+ # p.show()
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0104_pvti_send_simple_5pkt_big(self):
+ """v6o4 TX: Simple big packets: 5 -> 2"""
+ self.prepare_for_test("v4o4_5big_pkt")
+
+ send_pkts = self.create_packets(self.pg1, count=5, size=1050, is_ip6=True)
+ self.logger.info("count: %d " % len(send_pkts))
+ # self.logger.info(len(pkts))
+ rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=2)
+ for p in rx:
+ self.logger.info(p)
+ self.logger.info(len(p[PVTI].chunks[0].data))
+ # p.show()
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def Xtest_0204_pvti_send_simple_5pkt_mix(self):
+ """vXo4 TX: Simple packets mix: 5 -> 2"""
+ # FIXME: This test is disabled for now, but left here, to have this comment
+ # The mix of IPv4 and IPv6 packets in VPP will forward two
+ # different graphs, so after encap it will result in two
+ # PV packets: one with IPv4 chunks, and one with IPv6 chunks.
+ # The python test encapsulator does not do this, and it is probably
+ # a useless idea to introduce attempts to mimic this behavior,
+ # because in any case one can not expect the orderly scheduling
+ # of IPv4 vs IPv6 graph processing.
+ self.prepare_for_test("vXo4_5big_pkt")
+
+ send_pkts = self.create_packets(self.pg1, count=5, size=1050, af_mix=True)
+ # self.logger.info(len(pkts))
+ rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=2)
+ for p in rx:
+ self.logger.info(p)
+ self.logger.info(len(p[PVTI].chunks[0].data))
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0005_pvti_send_mix_3pkt_medium_mtu(self):
+ """TX: small+big+small packets over medium mtu: 3 -> 3"""
+ self.prepare_for_test("3pkt_small_mtu", underlay_mtu=400)
+
+ send_pkts = self.create_packets(self.pg1, count=3, size=500)
+ pkts = copy.deepcopy(send_pkts)
+ self.logger.info("count: %d " % len(send_pkts))
+ # self.logger.info(len(pkts))
+ rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=3)
+ for p in rx:
+ self.logger.info(p)
+ self.logger.info(len(p[PVTI].chunks[0].data))
+ # p.show()
+
+ # check the middle chunk which is spread across two packets
+ payload = rx[0][PVTI].chunks[1].data + rx[1][PVTI].chunks[0].data
+
+ pkt1 = IP(payload)
+
+ p0 = pkts[1][IP]
+ p0.ttl = p0.ttl - 1
+
+ pkt0 = IP(p0.build())
+ self.assertEqual(pkt0, pkt1)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0006_pvti_send_mix_4pkt_medium_mtu(self):
+ """TX: small+big+small packets over 600 mtu: 4 -> 3"""
+ self.prepare_for_test("6pkt_small_mtu", underlay_mtu=600)
+
+ send_pkts = self.create_packets(self.pg1, count=4, size=500)
+ pkts = copy.deepcopy(send_pkts)
+ # self.logger.info(len(pkts))
+ rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=3)
+ for p in rx:
+ self.logger.info(p)
+ self.logger.info(len(p[PVTI].chunks[0].data))
+ # p.show()
+
+ # check the middle chunk which is spread across two packets
+ payload = rx[0][PVTI].chunks[1].data + rx[1][PVTI].chunks[0].data
+
+ pkt1 = IP(payload)
+
+ p0 = pkts[1][IP]
+ p0.ttl = p0.ttl - 1
+
+ pkt0 = IP(p0.build())
+ self.assertEqual(pkt0, pkt1)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0007_pvti_send_simple_1_3_pkt(self):
+ """TX: Simple packet: 1 -> 3, small mtu"""
+
+ self.prepare_for_test("1_3_pkt_simple", underlay_mtu=520)
+ send_pkts = self.create_packets(self.pg1, count=1, size=1400)
+ pkts = copy.deepcopy(send_pkts)
+
+ rx = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=3)
+ for p in rx:
+ self.logger.info(p)
+
+ c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_0008_pvti_chained_1_3_pkt(self):
+ """TX: Chained packet: 2700 byte 1 -> 3, mtu 1000"""
+
+ self.prepare_for_test("1_3_pkt_simple", underlay_mtu=1000)
+ send_pkts = self.create_packets(self.pg1, count=1, size=2700)
+ pkts = copy.deepcopy(send_pkts)
+
+ pkt0 = Ether(raw(pkts[0]))[IP]
+
+ rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=3)
+ for p in rx:
+ self.logger.info(p)
+
+ p0 = pkts[0][IP]
+ p0.ttl = p0.ttl - 1
+ pkt0 = IP(p0.build())
+
+ payload = (
+ rx[0][PVTI].chunks[0].data
+ + rx[1][PVTI].chunks[0].data
+ + rx[2][PVTI].chunks[0].data
+ # + rx[2][PVTI].chunks[1].data
+ )
+ pkt1 = IP(payload)
+
+ self.assertEqual(pkt0, pkt1)
+
+ # FIXME: this will fail because the send path
+ # does not combine the data from two chained blocks.
+ # when this succeeds, the above checks in this testcase will need to be redone
+ # c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
+ # self.assertEqual(c_pkts, py_pkts)
+
+ self.cleanup_after_test()
+
+ def test_1001_pvti_rx_simple_1pkt(self):
+ """RX: Simple packet: 1 -> 32"""
+
+ self.prepare_for_test("1pkt_rx_simple")
+ pkts = self.create_rx_packets(self.pg1, rx_intf=self.pg0)
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show udp ports"))
+
+ recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=32)
+ for p in recv_pkts:
+ self.logger.info(p)
+
+ self.cleanup_after_test()
+
+ def test_1002_pvti_rx_big_1buf(self):
+ """RX: Orig Big packet, single buf: 2 -> 1"""
+
+ self.prepare_for_test("1buf_rx_big")
+
+ pkts_orig = self.create_packets(self.pg1, count=1, size=1900, for_rx=True)
+ pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show udp ports"))
+
+ known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
+
+ recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=1)
+ for i, p in enumerate(recv_pkts):
+ self.logger.info(p)
+ self.assertEqual(p[IP], known_good_pkts[i][IP])
+
+ self.cleanup_after_test()
+
+ def test_1003_pvti_rx_big_2buf(self):
+ """RX: Very Big packet, chained buf: 3 -> 1"""
+
+ self.prepare_for_test("2buf_rx_big")
+
+ pkts_orig = self.create_packets(self.pg1, count=1, size=3000, for_rx=True)
+
+ pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show udp ports"))
+
+ known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
+
+ recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=1)
+ for i, p in enumerate(recv_pkts):
+ self.logger.info(p)
+ if p[IP] != known_good_pkts[i][IP]:
+ p[IP].show()
+ known_good_pkts[i][IP].show()
+ self.assertEqual(p[IP], known_good_pkts[i][IP])
+
+ self.cleanup_after_test()
+
+ def test_1004_pvti_rx_big_2buf_and_small(self):
+ """RX: Very Big packet, chained buf: 3 -> 1 + small pkt"""
+
+ self.prepare_for_test("2buf_rx_big_and_small")
+
+ pkts_orig = self.create_packets(self.pg1, count=2, size=3000, for_rx=True)
+
+ pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show udp ports"))
+
+ known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
+
+ recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=2)
+ for i, p in enumerate(recv_pkts):
+ self.logger.info(p)
+ if p[IP] != known_good_pkts[i][IP]:
+ p[IP].show()
+ known_good_pkts[i][IP].show()
+ self.assertEqual(p[IP], known_good_pkts[i][IP])
+
+ self.cleanup_after_test()
+
+ def test_1005_pvti_rx_big_2buf_and_small_drop(self):
+ """RX: Very Big packet, chained buf: 3 -> 1 + small pkt, encap pkt lost"""
+
+ self.prepare_for_test("2buf_rx_big_and_small_drop")
+
+ pkts_orig = self.create_packets(self.pg1, count=3, size=3000, for_rx=True)
+
+ pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
+ # drop the second packet after encapsulation (the one with the second frag of the large packet)
+ pkts.pop(1)
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show udp ports"))
+
+ known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
+
+ # drop the large original packet, leaving just two small ones
+ known_good_pkts.pop(1)
+
+ recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=2)
+ for i, p in enumerate(recv_pkts):
+ self.logger.info(p)
+ if p[IP] != known_good_pkts[i][IP]:
+ p[IP].show()
+ known_good_pkts[i][IP].show()
+ self.assertEqual(p[IP], known_good_pkts[i][IP])
+
+ self.cleanup_after_test()
+
+ def test_1006_pvti_rx_big_2buf_and_small_drop2(self):
+ """RX: Very Big packet, chained buf: 3 -> 1 + small pkt, non-initial frag pkt lost"""
+
+ self.prepare_for_test("2buf_rx_big_and_small_drop2")
+
+ pkts_orig = self.create_packets(self.pg1, count=3, size=6000, for_rx=True)
+
+ pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
+ # drop the second packet after encapsulation (the one with the second frag of the large packet)
+ pkts.pop(2)
+ self.logger.info(self.vapi.cli("show pvti interface"))
+ self.logger.info(self.vapi.cli("show udp ports"))
+
+ known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
+ # drop the large original packet, leaving just two small ones
+ known_good_pkts.pop(1)
+
+ recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=2)
+ for i, p in enumerate(recv_pkts):
+ self.logger.info(p)
+ if p[IP] != known_good_pkts[i][IP]:
+ p[IP].show()
+ known_good_pkts[i][IP].show()
+ self.assertEqual(p[IP], known_good_pkts[i][IP])
+
+ self.cleanup_after_test()
+
+
+class PvtiHandoffTests(TestPvti):
+ """Pvti Tests in multi worker setup"""
+
+ vpp_worker_count = 2
+
+ def xtest_wg_peer_init(self):
+ """Handoff"""
+
+ port = 12383
+
+ # Create interfaces
+ wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
+ wg0.admin_up()
+ wg0.config_ip4()
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ peer_1 = VppWgPeer(
+ self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
+ ).add_vpp_config()
+ self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
+
+ r1 = VppIpRoute(
+ self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
+ ).add_vpp_config()
+
+ # skip the first automatic handshake
+ self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
+
+ # send a valid handsake init for which we expect a response
+ p = peer_1.mk_handshake(self.pg1)
+
+ rx = self.send_and_expect(self.pg1, [p], self.pg1)
+
+ peer_1.consume_response(rx[0])
+
+ # send a data packet from the peer through the tunnel
+ # this completes the handshake and pins the peer to worker 0
+ p = (
+ IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
+ / UDP(sport=222, dport=223)
+ / Raw()
+ )
+ d = peer_1.encrypt_transport(p)
+ p = peer_1.mk_tunnel_header(self.pg1) / (
+ Pvti(message_type=4, reserved_zero=0)
+ / PvtiTransport(
+ receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
+ )
+ )
+ rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
+ self.assertEqual(rx[IP].ttl, 19)
+
+ # send a packets that are routed into the tunnel
+ # and pins the peer tp worker 1
+ pe = (
+ Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
+ / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
+ / UDP(sport=555, dport=556)
+ / Raw(b"\x00" * 80)
+ )
+ rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
+ peer_1.validate_encapped(rxs, pe)
+
+ # send packets into the tunnel, from the other worker
+ p = [
+ (
+ peer_1.mk_tunnel_header(self.pg1)
+ / Pvti(message_type=4, reserved_zero=0)
+ / PvtiTransport(
+ receiver_index=peer_1.sender,
+ counter=ii + 1,
+ encrypted_encapsulated_packet=peer_1.encrypt_transport(
+ (
+ IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
+ / UDP(sport=222, dport=223)
+ / Raw()
+ )
+ ),
+ )
+ )
+ for ii in range(255)
+ ]
+
+ rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
+ self.assertEqual(rx[IP].ttl, 19)
+
+ # send a packets that are routed into the tunnel
+ # from worker 0
+ rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
+
+ peer_1.validate_encapped(rxs, pe)
+
+ r1.remove_vpp_config()
+ peer_1.remove_vpp_config()
+ wg0.remove_vpp_config()
diff --git a/test/test_reassembly.py b/test/test_reassembly.py
index 98c50f9bb61..caac7ce4df1 100644
--- a/test/test_reassembly.py
+++ b/test/test_reassembly.py
@@ -25,6 +25,7 @@ from util import ppp, fragment_rfc791, fragment_rfc8200
from vpp_gre_interface import VppGreInterface
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_papi import VppEnum
+from config import config
# 35 is enough to have >257 400-byte fragments
test_packet_count = 35
@@ -588,6 +589,9 @@ Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737"""
self.verify_capture(packets, dropped_packet_indexes)
self.src_if.assert_nothing_captured()
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_local_enable_disable(self):
"""local reassembly enabled/disable"""
self.vapi.ip_reassembly_enable_disable(
@@ -1675,6 +1679,9 @@ class TestIPv6Reassembly(VppTestCase):
)
rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_one_fragment(self):
"""whole packet in one fragment processed independently"""
pkt = (
@@ -1702,6 +1709,9 @@ class TestIPv6Reassembly(VppTestCase):
rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
self.assertNotIn(IPv6ExtHdrFragment, rx)
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_bunch_of_fragments(self):
"""valid fragments followed by rogue fragments and atomic fragment"""
pkt = (
@@ -1731,6 +1741,9 @@ class TestIPv6Reassembly(VppTestCase):
rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
self.assertNotIn(IPv6ExtHdrFragment, rx)
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_local_enable_disable(self):
"""local reassembly enabled/disable"""
self.vapi.ip_reassembly_enable_disable(
@@ -2366,6 +2379,9 @@ class TestIPv4ReassemblyLocalNode(VppTestCase):
index, seen, "Packet with packet_index %d not received" % index
)
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_reassembly(self):
"""basic reassembly"""
@@ -2492,6 +2508,9 @@ class TestFIFReassembly(VppTestCase):
"Packet with packet_index %d not received" % index,
)
+ @unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
+ )
def test_fif4(self):
"""Fragments in fragments (4o4)"""
@@ -2567,6 +2586,9 @@ class TestFIFReassembly(VppTestCase):
self.gre4.remove_vpp_config()
self.logger.debug(self.vapi.ppcli("show interface"))
+ @unittest.skipIf(
+ "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
+ )
def test_fif6(self):
"""Fragments in fragments (6o6)"""
# TODO this should be ideally in setUpClass, but then we hit a bug
diff --git a/test/test_span.py b/test/test_span.py
index 8eea1b0661d..b151ef4f7a1 100644
--- a/test/test_span.py
+++ b/test/test_span.py
@@ -3,7 +3,8 @@
import unittest
from scapy.packet import Raw
-from scapy.layers.l2 import Ether, GRE, ERSPAN
+from scapy.layers.l2 import Ether, GRE
+from scapy.contrib.erspan import ERSPAN
from scapy.layers.inet import IP, UDP
from scapy.layers.vxlan import VXLAN
@@ -14,6 +15,7 @@ from vpp_gre_interface import VppGreInterface
from vpp_vxlan_tunnel import VppVxlanTunnel
from collections import namedtuple
from vpp_papi import VppEnum
+from config import config
Tag = namedtuple("Tag", ["dot1", "vlan"])
@@ -21,6 +23,9 @@ DOT1AD = 0x88A8
DOT1Q = 0x8100
+@unittest.skipIf(
+ "vxlan" in config.excluded_plugins, "Exclude tests requiring VXLAN plugin"
+)
class TestSpan(VppTestCase):
"""SPAN Test Case"""
diff --git a/test/test_srv6_ad.py b/test/test_srv6_ad.py
index 5d7a621a9b8..69bdd313851 100644
--- a/test/test_srv6_ad.py
+++ b/test/test_srv6_ad.py
@@ -15,8 +15,10 @@ from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
from scapy.layers.inet import IP, UDP
from util import ppp
+from config import config
+@unittest.skipIf("srv6-ad" in config.excluded_plugins, "Exclude srv6-ad plugin tests")
class TestSRv6Ad(VppTestCase):
"""SRv6 Dynamic Proxy plugin Test Case"""
diff --git a/test/test_srv6_ad_flow.py b/test/test_srv6_ad_flow.py
index f776c71ac4b..f1e5109f472 100644
--- a/test/test_srv6_ad_flow.py
+++ b/test/test_srv6_ad_flow.py
@@ -14,8 +14,12 @@ from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
from scapy.layers.inet import IP, UDP
from util import ppp
+from config import config
+@unittest.skipIf(
+ "srv6-ad-flow" in config.excluded_plugins, "Exclude srv6-ad-flow plugin tests"
+)
class TestSRv6AdFlow(VppTestCase):
"""SRv6 Flow-based Dynamic Proxy plugin Test Case"""
diff --git a/test/test_srv6_as.py b/test/test_srv6_as.py
index 645cf338596..0f78159dccf 100644
--- a/test/test_srv6_as.py
+++ b/test/test_srv6_as.py
@@ -11,10 +11,12 @@ from scapy.packet import Raw
from scapy.layers.l2 import Ether, Dot1Q
from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
from scapy.layers.inet import IP, UDP
+from config import config
from util import ppp
+@unittest.skipIf("srv6-as" in config.excluded_plugins, "Exclude srv6-as plugin tests")
class TestSRv6As(VppTestCase):
"""SRv6 Static Proxy plugin Test Case"""
diff --git a/test/test_srv6_mobile.py b/test/test_srv6_mobile.py
index 314dfc114e2..7a96e84e34f 100644
--- a/test/test_srv6_mobile.py
+++ b/test/test_srv6_mobile.py
@@ -4,6 +4,7 @@ from framework import VppTestCase
from ipaddress import IPv4Address
from ipaddress import IPv6Address
from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto, VppIpTable
+from config import config
from vpp_srv6_mobile import (
SRv6MobileNhtype,
@@ -14,7 +15,12 @@ from vpp_srv6_mobile import (
from scapy.contrib.gtp import *
from scapy.all import *
+import unittest
+
+@unittest.skipIf(
+ "srv6-mobile" in config.excluded_plugins, "Exclude srv6-mobile plugin tests"
+)
class TestSRv6EndMGTP4E(VppTestCase):
"""SRv6 End.M.GTP4.E (SRv6 -> GTP-U)"""
@@ -108,6 +114,9 @@ class TestSRv6EndMGTP4E(VppTestCase):
self.assertEqual(pkt[GTP_U_Header].teid, 0xBBBBBBBB)
+@unittest.skipIf(
+ "srv6-mobile" in config.excluded_plugins, "Exclude srv6-mobile plugin tests"
+)
class TestSRv6TMGTP4D(VppTestCase):
"""SRv6 T.M.GTP4.D (GTP-U -> SRv6)"""
@@ -215,6 +224,9 @@ class TestSRv6TMGTP4D(VppTestCase):
)
+@unittest.skipIf(
+ "srv6-mobile" in config.excluded_plugins, "Exclude srv6-mobile plugin tests"
+)
class TestSRv6EndMGTP6E(VppTestCase):
"""SRv6 End.M.GTP6.E"""
@@ -306,6 +318,9 @@ class TestSRv6EndMGTP6E(VppTestCase):
self.assertEqual(pkt[GTP_U_Header].teid, 0xBBBBBBBB)
+@unittest.skipIf(
+ "srv6-mobile" in config.excluded_plugins, "Exclude srv6-mobile plugin tests"
+)
class TestSRv6EndMGTP6D(VppTestCase):
"""SRv6 End.M.GTP6.D"""
diff --git a/test/test_svs.py b/test/test_svs.py
index 1efc8fc846b..01173a201b7 100644
--- a/test/test_svs.py
+++ b/test/test_svs.py
@@ -12,10 +12,12 @@ from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
from vpp_papi import VppEnum
+from config import config
NUM_PKTS = 67
+@unittest.skipIf("svs" in config.excluded_plugins, "Exclude SVS plugin tests")
class TestSVS(VppTestCase):
"""SVS Test Case"""
diff --git a/test/test_trace_filter.py b/test/test_trace_filter.py
index c188631c200..45718d6f27f 100644
--- a/test/test_trace_filter.py
+++ b/test/test_trace_filter.py
@@ -8,6 +8,7 @@ from framework import VppTestCase
from asfframework import VppTestRunner
from vpp_papi import VppEnum
from vpp_ipsec import VppIpsecSA, VppIpsecSpd, VppIpsecSpdItfBinding, VppIpsecSpdEntry
+from config import config
from scapy.contrib.geneve import GENEVE
from scapy.packet import Raw
@@ -291,6 +292,9 @@ class TestTracefilter(TemplateTraceFilter):
self.assertEqual(len(pcap), 17)
+@unittest.skipIf(
+ "tracenode" in config.excluded_plugins, "Exclude tests requiring tracenode plugin"
+)
class TestTraceFilterInner(TemplateTraceFilter):
"""Packet Tracer Filter Inner Test"""
diff --git a/test/test_udp.py b/test/test_udp.py
index 34307ef1aab..6315f0efd5e 100644
--- a/test/test_udp.py
+++ b/test/test_udp.py
@@ -17,6 +17,7 @@ from vpp_ip_route import (
)
from vpp_neighbor import VppNeighbor
from vpp_papi import VppEnum
+from config import config
from scapy.packet import Raw
from scapy.layers.l2 import Ether
@@ -632,9 +633,9 @@ class TestUdpEncap(VppTestCase):
)
rx = self.send_and_expect(self.pg0, p_4 * NUM_PKTS, self.pg0)
- p_4 = IP(p_4["UDP"].payload)
+ p_4 = IP(bytes(p_4["UDP"].payload))
for p in rx:
- p = IP(p["Ether"].payload)
+ p = IP(bytes(p["Ether"].payload))
self.validate_inner4(p, p_4, ttl=63)
#
@@ -650,10 +651,10 @@ class TestUdpEncap(VppTestCase):
)
rx = self.send_and_expect(self.pg1, p_6 * NUM_PKTS, self.pg1)
- p_6 = IPv6(p_6["UDP"].payload)
- p = IPv6(rx[0]["Ether"].payload)
+ p_6 = IPv6(bytes(p_6["UDP"].payload))
+ p = IPv6(bytes(rx[0]["Ether"].payload))
for p in rx:
- p = IPv6(p["Ether"].payload)
+ p = IPv6(bytes(p["Ether"].payload))
self.validate_inner6(p, p_6, hlim=63)
#
@@ -672,13 +673,16 @@ class TestUdpEncap(VppTestCase):
self.pg2.enable_mpls()
rx = self.send_and_expect(self.pg2, p_mo4 * NUM_PKTS, self.pg2)
self.pg2.disable_mpls()
- p_mo4 = IP(MPLS(p_mo4["UDP"].payload).payload)
+ p_mo4 = IP(bytes(MPLS(bytes(p_mo4["UDP"].payload)).payload))
for p in rx:
- p = IP(p["Ether"].payload)
+ p = IP(bytes(p["Ether"].payload))
self.validate_inner4(p, p_mo4, ttl=63)
@tag_fixme_vpp_workers
+@unittest.skipIf(
+ "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
class TestUDP(VppTestCase):
"""UDP Test Case"""
@@ -721,6 +725,14 @@ class TestUDP(VppTestCase):
i.unconfig_ip4()
i.set_table_ip4(0)
i.admin_down()
+ # Unconfigure namespaces - remove our locks to the vrf tables
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="0", sw_if_index=self.loop0.sw_if_index
+ )
+ self.vapi.app_namespace_add_del_v4(
+ is_add=0, namespace_id="1", sw_if_index=self.loop1.sw_if_index
+ )
+
self.vapi.session_enable_disable(is_enable=0)
super(TestUDP, self).tearDown()
diff --git a/test/test_urpf.py b/test/test_urpf.py
index 1e4a6c5bb34..47a260081cc 100644
--- a/test/test_urpf.py
+++ b/test/test_urpf.py
@@ -11,10 +11,12 @@ from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
from vpp_papi import VppEnum
+from config import config
N_PKTS = 63
+@unittest.skipIf("urpf" in config.excluded_plugins, "Exclude URPF plugin tests")
class TestURPF(VppTestCase):
"""Unicast Reverse Path Forwarding Test Case"""
diff --git a/test/test_vlib.py b/test/test_vlib.py
index 1b92c94a4c4..48e32b6e669 100644
--- a/test/test_vlib.py
+++ b/test/test_vlib.py
@@ -252,6 +252,9 @@ class TestVlibFrameLeak(VppTestCase):
i.unconfig_ip4()
i.admin_down()
+ @unittest.skipIf(
+ "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
+ )
def test_vlib_mw_refork_frame_leak(self):
"""Vlib worker thread refork leak test case"""
icmp_id = 0xB
@@ -271,7 +274,7 @@ class TestVlibFrameLeak(VppTestCase):
rx = self.pg0.get_capture(1)
- self.assertEquals(len(rx), 1)
+ self.assertEqual(len(rx), 1)
rx = rx[0]
ether = rx[Ether]
ipv4 = rx[IP]
@@ -302,7 +305,7 @@ class TestVlibFrameLeak(VppTestCase):
rx = self.pg0.get_capture(1)
- self.assertEquals(len(rx), 1)
+ self.assertEqual(len(rx), 1)
rx = rx[0]
ether = rx[Ether]
ipv4 = rx[IP]
diff --git a/test/test_vrrp.py b/test/test_vrrp.py
index 8575016c326..41b8268e6bc 100644
--- a/test/test_vrrp.py
+++ b/test/test_vrrp.py
@@ -252,6 +252,7 @@ class VppVRRPVirtualRouter(VppObject):
return pkt
+@unittest.skipIf("vrrp" in config.excluded_plugins, "Exclude VRRP plugin tests")
class TestVRRP4(VppTestCase):
"""IPv4 VRRP Test Case"""
@@ -884,6 +885,7 @@ class TestVRRP4(VppTestCase):
self.assertEqual(rx[VRRPv3].addrlist, [vip])
+@unittest.skipIf("vrrp" in config.excluded_plugins, "Exclude VRRP plugin tests")
class TestVRRP6(VppTestCase):
"""IPv6 VRRP Test Case"""
diff --git a/test/test_vxlan.py b/test/test_vxlan.py
index 284e1359116..939a57f7343 100644
--- a/test/test_vxlan.py
+++ b/test/test_vxlan.py
@@ -20,8 +20,10 @@ from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_vxlan_tunnel import VppVxlanTunnel
from vpp_ip import INVALID_INDEX
from vpp_neighbor import VppNeighbor
+from config import config
+@unittest.skipIf("vxlan" in config.excluded_plugins, "Exclude VXLAN plugin tests")
class TestVxlan(BridgeDomain, VppTestCase):
"""VXLAN Test Case"""
@@ -480,6 +482,7 @@ class TestVxlan(BridgeDomain, VppTestCase):
self.logger.info(self.vapi.cli("show vxlan tunnel"))
+@unittest.skipIf("vxlan" in config.excluded_plugins, "Exclude VXLAN plugin tests")
class TestVxlan2(VppTestCase):
"""VXLAN Test Case"""
@@ -527,6 +530,7 @@ class TestVxlan2(VppTestCase):
rx = self.send_and_assert_no_replies(self.pg1, [p])
+@unittest.skipIf("vxlan" in config.excluded_plugins, "Exclude VXLAN plugin tests")
class TestVxlanL2Mode(VppTestCase):
"""VXLAN Test Case"""
diff --git a/test/test_vxlan6.py b/test/test_vxlan6.py
index 1bf01262a48..09a99604311 100644
--- a/test/test_vxlan6.py
+++ b/test/test_vxlan6.py
@@ -14,8 +14,10 @@ import util
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_vxlan_tunnel import VppVxlanTunnel
from vpp_ip import INVALID_INDEX
+from config import config
+@unittest.skipIf("vxlan" in config.excluded_plugins, "Exclude VXLAN plugin tests")
class TestVxlan6(BridgeDomain, VppTestCase):
"""VXLAN over IPv6 Test Case"""
@@ -256,7 +258,7 @@ class TestVxlan6(BridgeDomain, VppTestCase):
reassembled = util.reassemble4(payload)
- self.assertEqual(Ether(raw(frame))[IP], reassembled[IP])
+ self.assertEqual(Ether(bytes(frame))[IP], reassembled[IP])
"""
Tests with default port (4789)
diff --git a/test/test_wireguard.py b/test/test_wireguard.py
index e809daec163..481aa558312 100644
--- a/test/test_wireguard.py
+++ b/test/test_wireguard.py
@@ -261,7 +261,7 @@ class VppWgPeer(VppObject):
def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
self.verify_header(p, is_ip6)
- wg_pkt = Wireguard(p[Raw])
+ wg_pkt = Wireguard(bytes(p[Raw]))
if is_resp:
self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
@@ -310,7 +310,7 @@ class VppWgPeer(VppObject):
def consume_cookie(self, p, is_ip6=False):
self.verify_header(p, is_ip6)
- cookie_reply = Wireguard(p[Raw])
+ cookie_reply = Wireguard(bytes(p[Raw]))
self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
@@ -390,7 +390,7 @@ class VppWgPeer(VppObject):
self.noise_init(self.itf.public_key)
self.verify_header(p, is_ip6)
- init = Wireguard(p[Raw])
+ init = Wireguard(bytes(p[Raw]))
self._test.assertEqual(init[Wireguard].message_type, 1)
self._test.assertEqual(init[Wireguard].reserved_zero, 0)
@@ -438,9 +438,7 @@ class VppWgPeer(VppObject):
def consume_response(self, p, is_ip6=False):
self.verify_header(p, is_ip6)
-
- resp = Wireguard(p[Raw])
-
+ resp = Wireguard(bytes(p[Raw]))
self._test.assertEqual(resp[Wireguard].message_type, 2)
self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
self._test.assertEqual(
@@ -456,7 +454,7 @@ class VppWgPeer(VppObject):
def decrypt_transport(self, p, is_ip6=False):
self.verify_header(p, is_ip6)
- p = Wireguard(p[Raw])
+ p = Wireguard(bytes(p[Raw]))
self._test.assertEqual(p[Wireguard].message_type, 4)
self._test.assertEqual(p[Wireguard].reserved_zero, 0)
self._test.assertEqual(
@@ -501,7 +499,7 @@ class VppWgPeer(VppObject):
def is_handshake_init(p):
- wg_p = Wireguard(p[Raw])
+ wg_p = Wireguard(bytes(p[Raw]))
return wg_p[Wireguard].message_type == 1
diff --git a/test/vm_vpp_interfaces.py b/test/vm_vpp_interfaces.py
index 85417dcbca0..0f1e33d679b 100644
--- a/test/vm_vpp_interfaces.py
+++ b/test/vm_vpp_interfaces.py
@@ -489,11 +489,15 @@ class TestVPPInterfacesQemu:
except Exception:
pass
try:
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": layer3["ip4_vrf"]})
+ self.vapi.ip_table_add_del_v2(
+ is_add=0, table={"table_id": layer3["ip4_vrf"]}
+ )
except Exception:
pass
try:
- self.vapi.ip_table_add_del(is_add=0, table={"table_id": layer3["ip6_vrf"]})
+ self.vapi.ip_table_add_del_v2(
+ is_add=0, table={"table_id": layer3["ip6_vrf"]}
+ )
except Exception:
pass
try:
@@ -693,7 +697,7 @@ class TestVPPInterfacesQemu:
vrf_id -- vrf_id
"""
is_ipv6 = 0 if ip_version == 4 else 1
- self.vapi.ip_table_add_del(
+ self.vapi.ip_table_add_del_v2(
is_add=1, table={"table_id": vrf_id, "is_ip6": is_ipv6}
)
for sw_if_index, ip_prefix in if_idx_ip_prefixes:
diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py
index d36c56761e3..31aab3a03ee 100644
--- a/test/vpp_ip_route.py
+++ b/test/vpp_ip_route.py
@@ -169,16 +169,20 @@ def fib_interface_ip_prefix(test, addr, len, sw_if_index):
class VppIpTable(VppObject):
- def __init__(self, test, table_id, is_ip6=0, register=True, name=""):
+ def __init__(
+ self, test, table_id, is_ip6=0, register=True, name="", create_mfib=True
+ ):
self._test = test
self.name = name
self.table_id = table_id
self.is_ip6 = is_ip6
self.register = register
+ self.create_mfib = True
def add_vpp_config(self):
- self._test.vapi.ip_table_add_del(
+ self._test.vapi.ip_table_add_del_v2(
is_add=1,
+ create_mfib=self.create_mfib,
table={"is_ip6": self.is_ip6, "table_id": self.table_id, "name": self.name},
)
if self.register:
@@ -186,7 +190,7 @@ class VppIpTable(VppObject):
return self
def remove_vpp_config(self):
- self._test.vapi.ip_table_add_del(
+ self._test.vapi.ip_table_add_del_v2(
is_add=0, table={"is_ip6": self.is_ip6, "table_id": self.table_id}
)
diff --git a/test/vpp_pg_interface.py b/test/vpp_pg_interface.py
index cdb91ed1e41..1e02801cda9 100644
--- a/test/vpp_pg_interface.py
+++ b/test/vpp_pg_interface.py
@@ -187,7 +187,7 @@ class VppPGInterface(VppInterface):
self.test.logger.debug(
f"Generating testcase pg streams decode file: {pg_decode}"
)
- ts_opts = "-Vr"
+ ts_opts = "-Vxr"
for p in sorted(Path(pcap_dir).glob(f"{filename_prefix}*.pcap")):
self.test.logger.debug(f"Decoding {p}")
with open(f"{pg_decode}", "a", buffering=1) as f: