aboutsummaryrefslogtreecommitdiffstats
path: root/extras
diff options
context:
space:
mode:
Diffstat (limited to 'extras')
-rw-r--r--extras/bpf/Makefile15
-rw-r--r--extras/bpf/af_xdp.bpf.c103
-rw-r--r--extras/configs/pcapcli/setup.tracefilter2
-rwxr-xr-xextras/deb/mkdeb-octeon-roc46
-rw-r--r--extras/deprecated/dpdk-ipsec/cli.c9
-rw-r--r--extras/deprecated/netmap/FEATURE.yaml12
-rw-r--r--extras/deprecated/netmap/cli.c236
-rw-r--r--extras/deprecated/netmap/device.c252
-rw-r--r--extras/deprecated/netmap/net_netmap.h650
-rw-r--r--extras/deprecated/netmap/netmap.c315
-rw-r--r--extras/deprecated/netmap/netmap.h166
-rw-r--r--extras/deprecated/netmap/netmap_api.c137
-rw-r--r--extras/deprecated/netmap/node.c297
-rwxr-xr-xextras/deprecated/perfmon/intel_json_to_c.py64
-rw-r--r--extras/deprecated/plugins/gbp/CMakeLists.txt54
-rw-r--r--extras/deprecated/plugins/gbp/gbp.api470
-rw-r--r--extras/deprecated/plugins/gbp/gbp.h80
-rw-r--r--extras/deprecated/plugins/gbp/gbp_api.c1154
-rw-r--r--extras/deprecated/plugins/gbp/gbp_bridge_domain.c503
-rw-r--r--extras/deprecated/plugins/gbp/gbp_bridge_domain.h156
-rw-r--r--extras/deprecated/plugins/gbp/gbp_classify.c71
-rw-r--r--extras/deprecated/plugins/gbp/gbp_classify.h94
-rw-r--r--extras/deprecated/plugins/gbp/gbp_classify_node.c628
-rw-r--r--extras/deprecated/plugins/gbp/gbp_contract.c819
-rw-r--r--extras/deprecated/plugins/gbp/gbp_contract.h362
-rw-r--r--extras/deprecated/plugins/gbp/gbp_endpoint.c1595
-rw-r--r--extras/deprecated/plugins/gbp/gbp_endpoint.h376
-rw-r--r--extras/deprecated/plugins/gbp/gbp_endpoint_group.c402
-rw-r--r--extras/deprecated/plugins/gbp/gbp_endpoint_group.h166
-rw-r--r--extras/deprecated/plugins/gbp/gbp_ext_itf.c293
-rw-r--r--extras/deprecated/plugins/gbp/gbp_ext_itf.h92
-rw-r--r--extras/deprecated/plugins/gbp/gbp_fwd.c56
-rw-r--r--extras/deprecated/plugins/gbp/gbp_fwd_dpo.c306
-rw-r--r--extras/deprecated/plugins/gbp/gbp_fwd_dpo.h62
-rw-r--r--extras/deprecated/plugins/gbp/gbp_fwd_node.c163
-rw-r--r--extras/deprecated/plugins/gbp/gbp_itf.c574
-rw-r--r--extras/deprecated/plugins/gbp/gbp_itf.h97
-rw-r--r--extras/deprecated/plugins/gbp/gbp_learn.c76
-rw-r--r--extras/deprecated/plugins/gbp/gbp_learn.h63
-rw-r--r--extras/deprecated/plugins/gbp/gbp_learn_node.c718
-rw-r--r--extras/deprecated/plugins/gbp/gbp_policy.c79
-rw-r--r--extras/deprecated/plugins/gbp/gbp_policy.h57
-rw-r--r--extras/deprecated/plugins/gbp/gbp_policy_dpo.c420
-rw-r--r--extras/deprecated/plugins/gbp/gbp_policy_dpo.h121
-rw-r--r--extras/deprecated/plugins/gbp/gbp_policy_node.c341
-rw-r--r--extras/deprecated/plugins/gbp/gbp_recirc.c292
-rw-r--r--extras/deprecated/plugins/gbp/gbp_recirc.h88
-rw-r--r--extras/deprecated/plugins/gbp/gbp_route_domain.c447
-rw-r--r--extras/deprecated/plugins/gbp/gbp_route_domain.h84
-rw-r--r--extras/deprecated/plugins/gbp/gbp_scanner.c136
-rw-r--r--extras/deprecated/plugins/gbp/gbp_scanner.h (renamed from extras/libmemif/test/socket_test.h)21
-rw-r--r--extras/deprecated/plugins/gbp/gbp_subnet.c598
-rw-r--r--extras/deprecated/plugins/gbp/gbp_subnet.h53
-rw-r--r--extras/deprecated/plugins/gbp/gbp_types.h36
-rw-r--r--extras/deprecated/plugins/gbp/gbp_vxlan.c654
-rw-r--r--extras/deprecated/plugins/gbp/gbp_vxlan.h135
-rw-r--r--extras/deprecated/plugins/gbp/gbp_vxlan_node.c218
-rw-r--r--extras/deprecated/plugins/gbp/test_gbp.py7209
-rw-r--r--extras/deprecated/plugins/l2e/CMakeLists.txt28
-rw-r--r--extras/deprecated/plugins/l2e/l2e.api (renamed from extras/deprecated/netmap/netmap.api)37
-rw-r--r--extras/deprecated/plugins/l2e/l2e.c198
-rw-r--r--extras/deprecated/plugins/l2e/l2e.h84
-rw-r--r--extras/deprecated/plugins/l2e/l2e_api.c89
-rw-r--r--extras/deprecated/plugins/l2e/l2e_node.c283
-rw-r--r--extras/deprecated/vnet/lawful-intercept/lawful_intercept.c122
-rw-r--r--extras/deprecated/vnet/lawful-intercept/lawful_intercept.h54
-rw-r--r--extras/deprecated/vnet/lawful-intercept/node.c286
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/decap.c1050
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/dir.dox (renamed from extras/deprecated/netmap/dir.dox)11
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/encap.c601
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py304
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py91
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api100
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c1193
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h250
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c217
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def (renamed from extras/libmemif/test/main_test.h)16
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c60
-rw-r--r--extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h173
-rw-r--r--extras/deprecated/vom/test/test_vom.py23
-rw-r--r--extras/deprecated/vom/vom/cmd.hpp2
-rw-r--r--extras/deprecated/vom/vom/ra_config.hpp2
-rw-r--r--extras/deprecated/vom/vom/ra_prefix.hpp2
-rw-r--r--extras/deprecated/vppinfra/graph.c182
-rw-r--r--extras/deprecated/vppinfra/graph.h127
-rw-r--r--extras/deprecated/vppinfra/test_mheap.c286
-rw-r--r--extras/gomemif/WORKSPACE35
-rw-r--r--extras/gomemif/examples/BUILD.bazel21
-rw-r--r--extras/gomemif/examples/bridge.go286
-rw-r--r--extras/gomemif/examples/responder.go227
-rw-r--r--extras/gomemif/memif/BUILD.bazel17
-rw-r--r--extras/gomemif/memif/control_channel.go965
-rw-r--r--extras/gomemif/memif/control_channel_unsafe.go60
-rw-r--r--extras/gomemif/memif/interface.go507
-rw-r--r--extras/gomemif/memif/interface_unsafe.go40
-rw-r--r--extras/gomemif/memif/memif.go345
-rw-r--r--extras/gomemif/memif/memif_unsafe.go55
-rw-r--r--extras/gomemif/memif/packet_reader.go91
-rw-r--r--extras/gomemif/memif/packet_writer.go95
-rw-r--r--extras/gomemif/migrated.txt1
-rw-r--r--extras/hs-test/Makefile151
-rw-r--r--extras/hs-test/README.rst319
-rw-r--r--extras/hs-test/address_allocator.go98
-rw-r--r--extras/hs-test/container.go373
-rw-r--r--extras/hs-test/cpu.go90
-rw-r--r--extras/hs-test/docker/Dockerfile.build8
-rw-r--r--extras/hs-test/docker/Dockerfile.curl7
-rw-r--r--extras/hs-test/docker/Dockerfile.nginx20
-rw-r--r--extras/hs-test/docker/Dockerfile.nginx-http324
-rw-r--r--extras/hs-test/docker/Dockerfile.nginx-server12
-rw-r--r--extras/hs-test/docker/Dockerfile.vpp33
-rw-r--r--extras/hs-test/echo_test.go49
-rw-r--r--extras/hs-test/framework_test.go13
-rw-r--r--extras/hs-test/go.mod33
-rw-r--r--extras/hs-test/go.sum70
-rw-r--r--extras/hs-test/hst_suite.go503
-rw-r--r--extras/hs-test/http_test.go285
-rw-r--r--extras/hs-test/ldp_test.go84
-rw-r--r--extras/hs-test/linux_iperf_test.go40
-rw-r--r--extras/hs-test/mirroring_test.go24
-rw-r--r--extras/hs-test/netconfig.go383
-rw-r--r--extras/hs-test/proxy_test.go115
-rw-r--r--extras/hs-test/raw_session_test.go40
-rw-r--r--extras/hs-test/resources/cert/localhost.crt21
-rw-r--r--extras/hs-test/resources/cert/localhost.key28
-rw-r--r--extras/hs-test/resources/envoy/envoy.log (renamed from extras/gomemif/BUILD.bazel)0
-rw-r--r--extras/hs-test/resources/envoy/proxy.yaml53
-rw-r--r--extras/hs-test/resources/envoy/vcl.conf7
-rw-r--r--extras/hs-test/resources/nginx/html/index.html6
-rw-r--r--extras/hs-test/resources/nginx/nginx.conf30
-rw-r--r--extras/hs-test/resources/nginx/nginx_http3.conf26
-rw-r--r--extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf84
-rw-r--r--extras/hs-test/resources/nginx/nginx_server_mirroring.conf32
-rw-r--r--extras/hs-test/resources/nginx/vcl.conf11
-rwxr-xr-xextras/hs-test/script/build_boringssl.sh4
-rwxr-xr-xextras/hs-test/script/build_curl.sh5
-rwxr-xr-xextras/hs-test/script/build_hst.sh79
-rwxr-xr-xextras/hs-test/script/build_nginx.sh5
-rw-r--r--extras/hs-test/script/compress.sh33
-rwxr-xr-xextras/hs-test/script/nginx_ldp.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
-rw-r--r--extras/hs-test/test94
-rw-r--r--extras/hs-test/tools/http_server/http_server.go26
-rw-r--r--extras/hs-test/topo-containers/2peerVeth.yaml25
-rw-r--r--extras/hs-test/topo-containers/nginxProxyAndServer.yaml20
-rw-r--r--extras/hs-test/topo-containers/ns.yaml27
-rw-r--r--extras/hs-test/topo-containers/single.yaml47
-rw-r--r--extras/hs-test/topo-network/2peerVeth.yaml25
-rw-r--r--extras/hs-test/topo-network/2taps.yaml18
-rw-r--r--extras/hs-test/topo-network/ns.yaml23
-rw-r--r--extras/hs-test/topo-network/tap.yaml10
-rw-r--r--extras/hs-test/topo.go25
-rw-r--r--extras/hs-test/utils.go80
-rw-r--r--extras/hs-test/vars7
-rw-r--r--extras/hs-test/vcl_test.go156
-rw-r--r--extras/hs-test/vppinstance.go471
-rw-r--r--extras/lcov/README.md48
-rwxr-xr-xextras/lcov/lcov_post12
-rwxr-xr-xextras/lcov/lcov_prep7
-rw-r--r--extras/libmemif/CMakeLists.txt52
-rw-r--r--extras/libmemif/dockerfile10
-rw-r--r--extras/libmemif/docs/buildinstructions_doc.md49
-rw-r--r--extras/libmemif/docs/buildinstructions_doc.rst77
-rw-r--r--extras/libmemif/docs/devperftest_doc.md94
-rw-r--r--extras/libmemif/docs/gettingstarted_doc.md223
-rw-r--r--extras/libmemif/docs/gettingstarted_doc.rst234
-rw-r--r--extras/libmemif/examples/CMakeLists.txt20
-rw-r--r--extras/libmemif/examples/common/common.c192
-rw-r--r--extras/libmemif/examples/common/common.h120
-rw-r--r--extras/libmemif/examples/common/icmp_proto.c (renamed from extras/libmemif/examples/icmp_responder/icmp_proto.c)145
-rw-r--r--extras/libmemif/examples/common/icmp_proto.h (renamed from extras/libmemif/examples/icmp_responder/icmp_proto.h)12
-rw-r--r--extras/libmemif/examples/common/packet_handler.c85
-rw-r--r--extras/libmemif/examples/common/responder.c172
-rw-r--r--extras/libmemif/examples/common/sender.c55
-rw-r--r--extras/libmemif/examples/example_setup_doc.md207
-rw-r--r--extras/libmemif/examples/examples_doc.md18
-rw-r--r--extras/libmemif/examples/examples_doc.rst87
-rw-r--r--extras/libmemif/examples/icmp_responder-eb/main.c1069
-rw-r--r--extras/libmemif/examples/icmp_responder-epoll/main.c1320
-rw-r--r--extras/libmemif/examples/icmp_responder-mt/main.c914
-rw-r--r--extras/libmemif/examples/icmp_responder-mt_3-1/main.c549
-rw-r--r--extras/libmemif/examples/icmp_responder-zero-copy-slave/main.c1270
-rw-r--r--extras/libmemif/examples/icmp_responder/main.c502
-rw-r--r--extras/libmemif/examples/loopback/main.c307
-rw-r--r--extras/libmemif/examples/test_app/main.c324
-rw-r--r--extras/libmemif/libmemif_doc.md77
-rw-r--r--extras/libmemif/libmemif_doc.rst66
-rw-r--r--extras/libmemif/src/CMakeLists.txt1
-rw-r--r--extras/libmemif/src/libmemif.h420
-rw-r--r--extras/libmemif/src/main.c1890
-rw-r--r--extras/libmemif/src/memif_private.h134
-rw-r--r--extras/libmemif/src/socket.c820
-rw-r--r--extras/libmemif/src/socket.h56
-rw-r--r--extras/libmemif/test/CMakeLists.txt37
-rw-r--r--extras/libmemif/test/main_test.c945
-rw-r--r--extras/libmemif/test/socket_test.c658
-rw-r--r--extras/libmemif/test/suite_main/CMakeLists.txt18
-rw-r--r--extras/libmemif/test/suite_main/memif_main_test.c388
-rw-r--r--extras/libmemif/test/suite_socket/CMakeLists.txt18
-rw-r--r--extras/libmemif/test/suite_socket/memif_socket_test.c401
-rw-r--r--extras/libmemif/test/unit_test.c63
-rw-r--r--extras/libmemif/test/unit_test.h39
-rw-r--r--extras/packetforge/Edge.py91
-rw-r--r--extras/packetforge/EdgeAction.py55
-rw-r--r--extras/packetforge/ExpressionConverter.py162
-rw-r--r--extras/packetforge/ForgeResult.py45
-rw-r--r--extras/packetforge/InputFormat.py12
-rw-r--r--extras/packetforge/LICENSE.txt202
-rw-r--r--extras/packetforge/Node.py62
-rw-r--r--extras/packetforge/NodeAttribute.py65
-rw-r--r--extras/packetforge/NodeField.py86
-rw-r--r--extras/packetforge/ParseGraph.py179
-rw-r--r--extras/packetforge/Path.py35
-rw-r--r--extras/packetforge/PathNode.py39
-rw-r--r--extras/packetforge/PathNodeField.py37
-rw-r--r--extras/packetforge/ProtocolHeader.py387
-rw-r--r--extras/packetforge/ProtocolHeaderAttribute.py29
-rw-r--r--extras/packetforge/ProtocolHeaderField.py48
-rw-r--r--extras/packetforge/README.rst82
-rw-r--r--extras/packetforge/StringFormat.py11
-rw-r--r--extras/packetforge/flow_create.py173
-rw-r--r--extras/packetforge/flow_parse.py66
-rw-r--r--extras/packetforge/packetforge.py208
-rw-r--r--extras/packetforge/parsegraph/.gitattributes2
-rw-r--r--extras/packetforge/parsegraph/edges/ah_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ah_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/arpv4_after_macvlan.json11
-rw-r--r--extras/packetforge/parsegraph/edges/esp_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/esp_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/gre_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/gre_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/gtpc_after_udp.json11
-rw-r--r--extras/packetforge/parsegraph/edges/gtppsc_after_gtpu.json19
-rw-r--r--extras/packetforge/parsegraph/edges/gtpu_after_udp.json11
-rw-r--r--extras/packetforge/parsegraph/edges/icmp_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/icmpv6_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ip_after_gtppsc.json5
-rw-r--r--extras/packetforge/parsegraph/edges/ip_after_gtpu.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv4_after_geneve.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv4_after_gre.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv4_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv4_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv4_after_macvlan.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv4_after_vxlangpe.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_geneve.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_gre.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_gtppsc.json5
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_macvlan.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6_after_vxlangpe.json11
-rw-r--r--extras/packetforge/parsegraph/edges/ipv6srh_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/l2tpv2_after_udp.json11
-rw-r--r--extras/packetforge/parsegraph/edges/mac_after_geneve.json11
-rw-r--r--extras/packetforge/parsegraph/edges/mac_after_gre.json5
-rw-r--r--extras/packetforge/parsegraph/edges/mac_after_nvgre.json5
-rw-r--r--extras/packetforge/parsegraph/edges/mac_after_vxlan.json5
-rw-r--r--extras/packetforge/parsegraph/edges/mac_after_vxlangpe.json11
-rw-r--r--extras/packetforge/parsegraph/edges/pfcp_after_udp.json11
-rw-r--r--extras/packetforge/parsegraph/edges/sctp_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/sctp_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/tcp_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/tcp_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/tunnel_after_udp.json11
-rw-r--r--extras/packetforge/parsegraph/edges/udp_after_ipv4.json11
-rw-r--r--extras/packetforge/parsegraph/edges/udp_after_ipv6.json11
-rw-r--r--extras/packetforge/parsegraph/edges/vlan_after_macvlan.json15
-rw-r--r--extras/packetforge/parsegraph/nodes/ah.json26
-rw-r--r--extras/packetforge/parsegraph/nodes/arpv4.json54
-rw-r--r--extras/packetforge/parsegraph/nodes/esp.json14
-rw-r--r--extras/packetforge/parsegraph/nodes/geneve.json51
-rw-r--r--extras/packetforge/parsegraph/nodes/gre.json56
-rw-r--r--extras/packetforge/parsegraph/nodes/gtpc.json48
-rw-r--r--extras/packetforge/parsegraph/nodes/gtppsc.json32
-rw-r--r--extras/packetforge/parsegraph/nodes/gtpu.json63
-rw-r--r--extras/packetforge/parsegraph/nodes/icmp.json18
-rw-r--r--extras/packetforge/parsegraph/nodes/icmpv6.json18
-rw-r--r--extras/packetforge/parsegraph/nodes/ipv4.json76
-rw-r--r--extras/packetforge/parsegraph/nodes/ipv6.json47
-rw-r--r--extras/packetforge/parsegraph/nodes/ipv6crh16.json36
-rw-r--r--extras/packetforge/parsegraph/nodes/ipv6crh32.json32
-rw-r--r--extras/packetforge/parsegraph/nodes/ipv6srh.json40
-rw-r--r--extras/packetforge/parsegraph/nodes/l2tpv2ctl.json74
-rw-r--r--extras/packetforge/parsegraph/nodes/l2tpv2data.json79
-rw-r--r--extras/packetforge/parsegraph/nodes/mac.json22
-rw-r--r--extras/packetforge/parsegraph/nodes/nvgre.json52
-rw-r--r--extras/packetforge/parsegraph/nodes/payload.json18
-rw-r--r--extras/packetforge/parsegraph/nodes/pfcp.json58
-rw-r--r--extras/packetforge/parsegraph/nodes/sctp.json22
-rw-r--r--extras/packetforge/parsegraph/nodes/tcp.json79
-rw-r--r--extras/packetforge/parsegraph/nodes/udp.json24
-rw-r--r--extras/packetforge/parsegraph/nodes/vlan.json30
-rw-r--r--extras/packetforge/parsegraph/nodes/vxlan.json33
-rw-r--r--extras/packetforge/parsegraph/nodes/vxlangpe.json53
-rw-r--r--extras/packetforge/parsegraph/samples/mac_ipv4.json24
-rw-r--r--extras/packetforge/parsegraph/samples/mac_ipv4_udp.json27
-rw-r--r--extras/packetforge/parsegraph/samples/mac_ipv4_udp_gtpu_gtppsc_ipv4.json43
-rw-r--r--extras/packetforge/parsegraph/samples/mac_ipv4_udp_vxlan_mac_ipv4.json43
-rw-r--r--extras/packetforge/parsegraph/samples/mac_ipv6.json44
-rw-r--r--extras/packetforge/parsegraph/samples/mac_vlan_ipv4.json34
-rw-r--r--extras/packetforge/parsegraph/spec.md533
-rw-r--r--extras/rpm/Makefile9
-rw-r--r--extras/rpm/opensuse/Dockerfile16
-rw-r--r--extras/rpm/opensuse/README.md89
-rw-r--r--extras/rpm/opensuse/vpp.spec320
-rw-r--r--extras/rpm/vpp.spec53
-rwxr-xr-xextras/scripts/build_static_vppctl.sh22
-rwxr-xr-xextras/scripts/check_documentation.sh67
-rwxr-xr-xextras/scripts/checkstyle.sh15
-rwxr-xr-xextras/scripts/crcchecker.py221
-rwxr-xr-xextras/scripts/list_api_changes.py23
-rwxr-xr-xextras/scripts/lsnet63
-rwxr-xr-xextras/scripts/vpp-review240
-rw-r--r--extras/selinux/selinux_doc.md506
-rw-r--r--extras/selinux/selinux_doc.rst554
-rw-r--r--extras/selinux/vpp-custom.te17
-rw-r--r--extras/snap/README.md86
-rw-r--r--extras/snap/README.rst84
-rw-r--r--extras/strongswan/README.md23
-rw-r--r--extras/strongswan/README.rst31
-rw-r--r--extras/strongswan/configs/responder_nat/vpp.conf2
-rw-r--r--extras/strongswan/vpp_sswan/Makefile116
-rw-r--r--extras/strongswan/vpp_sswan/README.rst170
-rw-r--r--extras/strongswan/vpp_sswan/docker/Dockerfile27
-rw-r--r--extras/strongswan/vpp_sswan/docker/configs/startup.conf32
-rw-r--r--extras/strongswan/vpp_sswan/docker/configs/swanctl_docker1.conf35
-rw-r--r--extras/strongswan/vpp_sswan/docker/configs/swanctl_docker2.conf35
-rw-r--r--extras/strongswan/vpp_sswan/docker/configs/vpp.conf8
-rwxr-xr-xextras/strongswan/vpp_sswan/docker/exposedockernetns.sh14
-rwxr-xr-xextras/strongswan/vpp_sswan/docker/init_containers.sh70
-rwxr-xr-xextras/strongswan/vpp_sswan/docker/run.sh118
-rw-r--r--extras/strongswan/vpp_sswan/docker/scripts/init.sh8
-rw-r--r--extras/strongswan/vpp_sswan/docker/scripts/init_docker1.sh24
-rw-r--r--extras/strongswan/vpp_sswan/docker/scripts/init_docker2.sh23
-rw-r--r--extras/strongswan/vpp_sswan/docker/scripts/run_vpp.sh13
-rw-r--r--extras/strongswan/vpp_sswan/kernel-vpp.conf7
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c1997
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h41
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_net.c773
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_net.h41
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_plugin.c103
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_plugin.h34
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_shared.c629
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_shared.h93
-rw-r--r--extras/strongswan/vpp_sswan/swanctl.conf35
-rwxr-xr-xextras/vagrant/build.sh4
-rwxr-xr-xextras/vagrant/clearinterfaces.sh2
-rwxr-xr-xextras/vagrant/install.sh2
-rwxr-xr-xextras/vagrant/run.sh2
-rwxr-xr-xextras/vagrant/update.sh2
-rwxr-xr-xextras/vagrant/vcl_test.sh2
-rw-r--r--extras/vcl-ldpreload/README.md27
-rw-r--r--extras/vcl-ldpreload/README.rst40
-rw-r--r--extras/vpp_config/README.rst1031
-rw-r--r--extras/vpp_config/data/startup.conf.template4
-rwxr-xr-xextras/vpp_config/scripts/dpdk-devbind.py213
-rw-r--r--extras/vpp_config/setup.py60
-rwxr-xr-xextras/vpp_config/vpp_config.py354
-rw-r--r--extras/vpp_config/vpplib/AutoConfig.py1205
-rw-r--r--extras/vpp_config/vpplib/CpuUtils.py88
-rw-r--r--extras/vpp_config/vpplib/QemuUtils.py484
-rw-r--r--extras/vpp_config/vpplib/VPPUtil.py435
-rw-r--r--extras/vpp_config/vpplib/VppGrubUtil.py146
-rw-r--r--extras/vpp_config/vpplib/VppHugePageUtil.py76
-rw-r--r--extras/vpp_config/vpplib/VppPCIUtil.py165
-rw-r--r--extras/vpp_config/vpplib/constants.py18
-rwxr-xr-xextras/vpp_if_stats/README.md35
-rw-r--r--extras/vpp_if_stats/README.rst40
-rwxr-xr-xextras/vpp_stats_fs/README.md113
-rw-r--r--extras/vpp_stats_fs/README.rst148
-rw-r--r--extras/vpptop/README.md25
-rw-r--r--extras/vpptop/README.rst36
376 files changed, 48284 insertions, 18184 deletions
diff --git a/extras/bpf/Makefile b/extras/bpf/Makefile
index 77b06434237..9ad26eaacb1 100644
--- a/extras/bpf/Makefile
+++ b/extras/bpf/Makefile
@@ -1,13 +1,14 @@
-CC?=clang
+CC := $(shell which clang)
+
# where to find bpf includes?
-BPF_ROOT?=/usr/include
-#BPF_ROOT?=/opt/vpp/external/x86_64/include
+BPF_ROOT ?= /usr/include
+#BPF_ROOT ?= /opt/vpp/external/x86_64/include
-CFLAGS:=-O3 -g -Wextra -Wall -target bpf
+CFLAGS := -O3 -g -Wextra -Wall -target bpf
# Workaround for Ubuntu/Debian for asm/types.h
-CFLAGS+= -I/usr/include/x86_64-linux-gnu
-CFLAGS+= -I$(BPF_ROOT)
-#CFLAGS+= -DDEBUG
+CFLAGS += -I/usr/include/x86_64-linux-gnu
+CFLAGS += -I$(BPF_ROOT)
+#CFLAGS += -DDEBUG
all: af_xdp.bpf.o
diff --git a/extras/bpf/af_xdp.bpf.c b/extras/bpf/af_xdp.bpf.c
index eddd2b0e509..c9ca0193ce5 100644
--- a/extras/bpf/af_xdp.bpf.c
+++ b/extras/bpf/af_xdp.bpf.c
@@ -4,11 +4,15 @@
* Copyright (c) 2020 Cisco and/or its affiliates.
*/
#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <xdp/xdp_helpers.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
-#include <bpf/bpf_helpers.h>
+
+#define XDP_METADATA_SECTION "xdp_metadata"
+#define XSK_PROG_VERSION 1
/*
* when compiled, debug print can be viewed with eg.
@@ -26,63 +30,70 @@
#define DEBUG_PRINT(fmt, ...)
#endif /* DEBUG */
-#define ntohs(x) __constant_ntohs(x)
+#define ntohs(x) __constant_ntohs (x)
-SEC("maps")
-struct bpf_map_def xsks_map = {
- .type = BPF_MAP_TYPE_XSKMAP,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 64, /* max 64 queues per device */
-};
+#define DEFAULT_QUEUE_IDS 64
-SEC("xdp_sock")
-int xdp_sock_prog(struct xdp_md *ctx) {
- const void *data = (void *)(long)ctx->data;
- const void *data_end = (void *)(long)ctx->data_end;
+struct
+{
+ __uint (type, BPF_MAP_TYPE_XSKMAP);
+ __uint (key_size, sizeof (int));
+ __uint (value_size, sizeof (int));
+ __uint (max_entries, DEFAULT_QUEUE_IDS);
+} xsks_map SEC (".maps");
- DEBUG_PRINT("rx %ld bytes packet", (long)data_end - (long)data);
+struct
+{
+ __uint (priority, 10);
+ __uint (XDP_PASS, 1);
+} XDP_RUN_CONFIG (xdp_sock_prog);
- /* smallest packet we are interesting in is ip-ip */
- if (data + sizeof(struct ethhdr) + 2 * sizeof(struct iphdr) > data_end) {
- DEBUG_PRINT("packet too small");
- return XDP_PASS;
- }
+SEC ("xdp")
+int
+xdp_sock_prog (struct xdp_md *ctx)
+{
+ const void *data = (void *) (long) ctx->data;
+ const void *data_end = (void *) (long) ctx->data_end;
+
+ DEBUG_PRINT ("rx %ld bytes packet", (long) data_end - (long) data);
- const struct ethhdr *eth = data;
- if (eth->h_proto != ntohs(ETH_P_IP)) {
- DEBUG_PRINT("unsupported eth proto %x", (int)eth->h_proto);
- return XDP_PASS;
+ /* smallest packet we are interesting in is ip-ip */
+ if (data + sizeof (struct ethhdr) + 2 * sizeof (struct iphdr) > data_end)
+ {
+ DEBUG_PRINT ("packet too small");
+ return XDP_PASS;
}
- const struct iphdr *ip = (void *)(eth + 1);
- switch (ip->protocol) {
- case IPPROTO_UDP: {
- const struct udphdr *udp = (void *)(ip + 1);
- if (udp->dest != ntohs(4789)) { /* VxLAN dest port */
- DEBUG_PRINT("unsupported udp dst port %x", (int)udp->dest);
- return XDP_PASS;
- }
- }
- case IPPROTO_IPIP:
- case IPPROTO_ESP:
- break;
- default:
- DEBUG_PRINT("unsupported ip proto %x", (int)ip->protocol);
- return XDP_PASS;
+ const struct ethhdr *eth = data;
+ if (eth->h_proto != ntohs (ETH_P_IP))
+ {
+ DEBUG_PRINT ("unsupported eth proto %x", (int) eth->h_proto);
+ return XDP_PASS;
}
- int qid = ctx->rx_queue_index;
- if (!bpf_map_lookup_elem(&xsks_map, &qid))
+ const struct iphdr *ip = (void *) (eth + 1);
+ switch (ip->protocol)
+ {
+ case IPPROTO_UDP:
{
- DEBUG_PRINT("no socket found");
- return XDP_PASS;
+ const struct udphdr *udp = (void *) (ip + 1);
+ if (udp->dest != ntohs (4789)) /* VxLAN dest port */
+ {
+ DEBUG_PRINT ("unsupported udp dst port %x", (int) udp->dest);
+ return XDP_PASS;
+ }
}
+ case IPPROTO_IPIP:
+ case IPPROTO_ESP:
+ break;
+ default:
+ DEBUG_PRINT ("unsupported ip proto %x", (int) ip->protocol);
+ return XDP_PASS;
+ }
- DEBUG_PRINT("going to socket %d", qid);
- return bpf_redirect_map(&xsks_map, qid, 0);
+ return bpf_redirect_map (&xsks_map, ctx->rx_queue_index, XDP_PASS);
}
/* actually Dual GPLv2/Apache2, but GPLv2 as far as kernel is concerned */
-SEC("license")
-char _license[] = "GPL";
+char _license[] SEC ("license") = "GPL";
+__uint (xsk_prog_version, XSK_PROG_VERSION) SEC (XDP_METADATA_SECTION);
diff --git a/extras/configs/pcapcli/setup.tracefilter b/extras/configs/pcapcli/setup.tracefilter
index db07a0e4224..18a55e16ab0 100644
--- a/extras/configs/pcapcli/setup.tracefilter
+++ b/extras/configs/pcapcli/setup.tracefilter
@@ -17,6 +17,6 @@ packet-generator new {
}
}
-comment { Pick one, uncomment, and "pcap rx ..." or "trace add pg-input ..." }
+comment { Pick one, uncomment, and "pcap trace rx ..." or "trace add pg-input ..." }
comment { classify filter trace mask l3 ip4 src match l3 ip4 src 192.168.1.15 }
comment { classify filter pcap mask l3 ip4 src match l3 ip4 src 192.168.1.15 }
diff --git a/extras/deb/mkdeb-octeon-roc b/extras/deb/mkdeb-octeon-roc
new file mode 100755
index 00000000000..08e57f4bd1e
--- /dev/null
+++ b/extras/deb/mkdeb-octeon-roc
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+PKG=vpp-dep-octeon-roc
+URL=https://github.com/MarvellEmbeddedProcessors/marvell-vpp.git
+ARCH=$(dpkg --print-architecture)
+TMP_DIR=$(mktemp -d -p $PWD)
+
+set -eEuo pipefail
+
+err_handler()
+{
+ trap '' INT TERM EXIT ERR
+ echo "Cleaning up ${TMP_DIR}"
+ rm -rf ${TMP_DIR}
+ exit
+}
+trap "err_handler" INT TERM EXIT ERR
+
+SRC=${TMP_DIR}/src
+BUILD=${TMP_DIR}/build
+STAGE=${TMP_DIR}/pkg
+INSTALL_PREFIX=/opt/vpp/external/$(uname -m)
+
+git clone ${URL} ${SRC}
+VER=0.0.$(git -C ${SRC} rev-list --count HEAD)
+
+cmake -S ${SRC} -B ${BUILD}
+cmake --build ${BUILD} --parallel
+cmake --install ${BUILD} --prefix ${STAGE}${INSTALL_PREFIX}
+
+mkdir -p ${STAGE}/DEBIAN
+
+cat > ${STAGE}/DEBIAN/control << __EOF__
+Package: ${PKG}
+Version: ${VER}
+Architecture: ${ARCH}
+Maintainer: vpp-dev <vpp-dev@fd.io>
+Installed-Size: $(du -ks ${STAGE}|cut -f 1)
+Section: system
+Priority: extra
+Description: Marvell Octeon ROC library for VPP
+ See https://github.com/MarvellEmbeddedProcessors/marvell-vpp
+__EOF__
+
+DEB=${PKG}_${VER}_${ARCH}.deb
+dpkg-deb -b ${STAGE} ${DEB}
diff --git a/extras/deprecated/dpdk-ipsec/cli.c b/extras/deprecated/dpdk-ipsec/cli.c
index 8fdda020a77..2c8647576b0 100644
--- a/extras/deprecated/dpdk-ipsec/cli.c
+++ b/extras/deprecated/dpdk-ipsec/cli.c
@@ -141,11 +141,9 @@ clear_crypto_stats_fn (vlib_main_t * vm, unformat_input_t * input,
*
* @cliexpar
* Example of how to clear the DPDK Crypto device statistics:
- * @cliexsart{clear dpdk crypto devices statistics}
+ * @cliexstart{clear dpdk crypto devices statistics}
* vpp# clear dpdk crypto devices statistics
* @cliexend
- * Example of clearing the DPDK Crypto device statistic data:
- * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (clear_dpdk_crypto_stats, static) = {
@@ -176,12 +174,13 @@ show_dpdk_crypto_fn (vlib_main_t * vm, unformat_input_t * input,
*
* @cliexpar
* Example of how to display the DPDK Crypto device information:
- * @cliexsart{show dpdk crypto devices}
+ * @cliexstart{show dpdk crypto devices}
* vpp# show dpdk crypto devices
* aesni_mb0 crypto_aesni_mb up
* numa_node 0, max_queues 4
* SYMMETRIC_CRYPTO, SYM_OPERATION_CHAINING, CPU_AVX2, CPU_AESNI
- * Cipher: aes-cbc-128, aes-cbc-192, aes-cbc-256, aes-ctr-128, aes-ctr-192, aes-ctr-256, aes-gcm-128, aes-gcm-192, aes-gcm-256
+ * Cipher: aes-cbc-128, aes-cbc-192, aes-cbc-256, aes-ctr-128, aes-ctr-192,
+ * aes-ctr-256, aes-gcm-128, aes-gcm-192, aes-gcm-256
* Auth: md5-96, sha1-96, sha-256-128, sha-384-192, sha-512-256
* enqueue 2 dequeue 2 enqueue_err 0 dequeue_err 0
* free_resources 3 :
diff --git a/extras/deprecated/netmap/FEATURE.yaml b/extras/deprecated/netmap/FEATURE.yaml
deleted file mode 100644
index e23e5c243e7..00000000000
--- a/extras/deprecated/netmap/FEATURE.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-name: Netmap Device
-maintainer: Damjan Marion <damarion@cisco.com>
-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."
-missing:
- - API dump
-state: production
-properties: [API, CLI, STATS, MULTITHREAD]
diff --git a/extras/deprecated/netmap/cli.c b/extras/deprecated/netmap/cli.c
deleted file mode 100644
index 713632947a1..00000000000
--- a/extras/deprecated/netmap/cli.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2016 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 <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-
-#include <vnet/devices/netmap/net_netmap.h>
-#include <vnet/devices/netmap/netmap.h>
-
-static clib_error_t *
-netmap_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;
- u8 *host_if_name = NULL;
- u8 hwaddr[6];
- u8 *hw_addr_ptr = 0;
- int r;
- u8 is_pipe = 0;
- u8 is_master = 0;
- u32 sw_if_index = ~0;
- clib_error_t *error = NULL;
-
- /* 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", &host_if_name))
- ;
- else
- if (unformat
- (line_input, "hw-addr %U", unformat_ethernet_address, hwaddr))
- hw_addr_ptr = hwaddr;
- else if (unformat (line_input, "pipe"))
- is_pipe = 1;
- else if (unformat (line_input, "master"))
- is_master = 1;
- else if (unformat (line_input, "slave"))
- is_master = 0;
- else
- {
- error = clib_error_return (0, "unknown input `%U'",
- format_unformat_error, line_input);
- goto done;
- }
- }
-
- if (host_if_name == NULL)
- {
- error = clib_error_return (0, "missing host interface name");
- goto done;
- }
-
- r =
- netmap_create_if (vm, host_if_name, hw_addr_ptr, is_pipe, is_master,
- &sw_if_index);
-
- if (r == VNET_API_ERROR_SYSCALL_ERROR_1)
- {
- error = clib_error_return (0, "%s (errno %d)", strerror (errno), errno);
- goto done;
- }
-
- if (r == VNET_API_ERROR_INVALID_INTERFACE)
- {
- error = clib_error_return (0, "Invalid interface name");
- goto done;
- }
-
- if (r == VNET_API_ERROR_SUBIF_ALREADY_EXISTS)
- {
- error = clib_error_return (0, "Interface already exists");
- goto done;
- }
-
- vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
- sw_if_index);
-
-done:
- unformat_free (line_input);
-
- return error;
-}
-
-/*?
- * '<em>netmap</em>' is a framework for very fast packet I/O from userspace.
- * '<em>VALE</em>' is an equally fast in-kernel software switch using the
- * netmap API. '<em>netmap</em>' includes '<em>netmap pipes</em>', a shared
- * memory packet transport channel. Together, they provide 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. Netmap/VALE
- * generates the '<em>netmap.ko</em>' kernel module that needs to be loaded
- * before netmap interfaces can be created.
- * - https://github.com/luigirizzo/netmap - Netmap/VALE repo.
- * - https://github.com/vpp-dev/netmap - VPP development package for Netmap/VALE,
- * which is a snapshot of the Netmap/VALE repo with minor changes to work
- * with containers and modified kernel drivers to work with NICs.
- *
- * Create a netmap interface that will attach to a linux interface.
- * The interface must already exist. Once created, a new netmap interface
- * will exist in VPP with the name '<em>netmap-<ifname></em>', where
- * '<em><ifname></em>' takes one of two forms:
- * - <b>ifname</b> - Linux interface to bind too.
- * - <b>valeXXX:YYY</b> -
- * - Where '<em>valeXXX</em>' is an arbitrary name for a VALE
- * interface that must start with '<em>vale</em>' and is less
- * than 16 characters.
- * - Where '<em>YYY</em>' is an existing linux namespace.
- *
- * This command has the following optional parameters:
- *
- * - <b>hw-addr <mac-addr></b> - Optional ethernet address, can be in either
- * X:X:X:X:X:X unix or X.X.X cisco format.
- *
- * - <b>pipe</b> - Optional flag to indicate that a '<em>netmap pipe</em>'
- * instance should be created.
- *
- * - <b>master | slave</b> - Optional flag to indicate whether VPP should
- * be the master or slave of the '<em>netmap pipe</em>'. Only considered
- * if '<em>pipe</em>' is entered. Defaults to '<em>slave</em>' if not entered.
- *
- * @cliexpar
- * Example of how to create a netmap interface tied to the linux
- * namespace '<em>vpp1</em>':
- * @cliexstart{create netmap name vale00:vpp1 hw-addr 02:FE:3F:34:15:9B pipe master}
- * netmap-vale00:vpp1
- * @cliexend
- * Once the netmap interface is created, enable the interface using:
- * @cliexcmd{set interface state netmap-vale00:vpp1 up}
-?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (netmap_create_command, static) = {
- .path = "create netmap",
- .short_help = "create netmap name <ifname>|valeXXX:YYY "
- "[hw-addr <mac-addr>] [pipe] [master|slave]",
- .function = netmap_create_command_fn,
-};
-/* *INDENT-ON* */
-
-static clib_error_t *
-netmap_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;
- u8 *host_if_name = NULL;
- clib_error_t *error = NULL;
-
- /* 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", &host_if_name))
- ;
- else
- {
- error = clib_error_return (0, "unknown input `%U'",
- format_unformat_error, line_input);
- goto done;
- }
- }
-
- if (host_if_name == NULL)
- {
- error = clib_error_return (0, "missing host interface name");
- goto done;
- }
-
- netmap_delete_if (vm, host_if_name);
-
-done:
- unformat_free (line_input);
-
- return error;
-}
-
-/*?
- * Delete a netmap interface. Use the '<em><ifname></em>' to identify
- * the netmap interface to be deleted. In VPP, netmap interfaces are
- * named as '<em>netmap-<ifname></em>', where '<em><ifname></em>'
- * takes one of two forms:
- * - <b>ifname</b> - Linux interface to bind too.
- * - <b>valeXXX:YYY</b> -
- * - Where '<em>valeXXX</em>' is an arbitrary name for a VALE
- * interface that must start with '<em>vale</em>' and is less
- * than 16 characters.
- * - Where '<em>YYY</em>' is an existing linux namespace.
- *
- * @cliexpar
- * Example of how to delete a netmap interface named '<em>netmap-vale00:vpp1</em>':
- * @cliexcmd{delete netmap name vale00:vpp1}
-?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (netmap_delete_command, static) = {
- .path = "delete netmap",
- .short_help = "delete netmap name <ifname>|valeXXX:YYY",
- .function = netmap_delete_command_fn,
-};
-/* *INDENT-ON* */
-
-clib_error_t *
-netmap_cli_init (vlib_main_t * vm)
-{
- return 0;
-}
-
-VLIB_INIT_FUNCTION (netmap_cli_init);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/extras/deprecated/netmap/device.c b/extras/deprecated/netmap/device.c
deleted file mode 100644
index 47407aaaa26..00000000000
--- a/extras/deprecated/netmap/device.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2016 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 <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-
-#include <vnet/devices/netmap/net_netmap.h>
-#include <vnet/devices/netmap/netmap.h>
-
-#define foreach_netmap_tx_func_error \
-_(NO_FREE_SLOTS, "no free tx slots") \
-_(PENDING_MSGS, "pending msgs in tx ring")
-
-typedef enum
-{
-#define _(f,s) NETMAP_TX_ERROR_##f,
- foreach_netmap_tx_func_error
-#undef _
- NETMAP_TX_N_ERROR,
-} netmap_tx_func_error_t;
-
-static char *netmap_tx_func_error_strings[] = {
-#define _(n,s) s,
- foreach_netmap_tx_func_error
-#undef _
-};
-
-
-static u8 *
-format_netmap_device_name (u8 * s, va_list * args)
-{
- u32 i = va_arg (*args, u32);
- netmap_main_t *apm = &netmap_main;
- netmap_if_t *nif = pool_elt_at_index (apm->interfaces, i);
-
- s = format (s, "netmap-%s", nif->host_if_name);
- return s;
-}
-
-static u8 *
-format_netmap_device (u8 * s, va_list * args)
-{
- u32 dev_instance = va_arg (*args, u32);
- int verbose = va_arg (*args, int);
- netmap_main_t *nm = &netmap_main;
- netmap_if_t *nif = vec_elt_at_index (nm->interfaces, dev_instance);
- u32 indent = format_get_indent (s);
-
- s = format (s, "NETMAP interface");
- if (verbose)
- {
- s = format (s, "\n%U version %d flags 0x%x"
- "\n%U region %u memsize 0x%x offset 0x%x"
- "\n%U tx_slots %u rx_slots %u tx_rings %u rx_rings %u",
- format_white_space, indent + 2,
- nif->req->nr_version,
- nif->req->nr_flags,
- format_white_space, indent + 2,
- nif->mem_region,
- nif->req->nr_memsize,
- nif->req->nr_offset,
- format_white_space, indent + 2,
- nif->req->nr_tx_slots,
- nif->req->nr_rx_slots,
- nif->req->nr_tx_rings, nif->req->nr_rx_rings);
- }
- return s;
-}
-
-static u8 *
-format_netmap_tx_trace (u8 * s, va_list * args)
-{
- s = format (s, "Unimplemented...");
- return s;
-}
-
-VNET_DEVICE_CLASS_TX_FN (netmap_device_class) (vlib_main_t * vm,
- vlib_node_runtime_t * node,
- vlib_frame_t * frame)
-{
- netmap_main_t *nm = &netmap_main;
- u32 *buffers = vlib_frame_vector_args (frame);
- u32 n_left = frame->n_vectors;
- f64 const time_constant = 1e3;
- vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
- netmap_if_t *nif = pool_elt_at_index (nm->interfaces, rd->dev_instance);
- int cur_ring;
-
- clib_spinlock_lock_if_init (&nif->lockp);
-
- cur_ring = nif->first_tx_ring;
-
- while (n_left && cur_ring <= nif->last_tx_ring)
- {
- struct netmap_ring *ring = NETMAP_TXRING (nif->nifp, cur_ring);
- int n_free_slots = nm_ring_space (ring);
- uint cur = ring->cur;
-
- if (nm_tx_pending (ring))
- {
- if (ioctl (nif->fd, NIOCTXSYNC, NULL) < 0)
- clib_unix_warning ("NIOCTXSYNC");
- clib_cpu_time_wait (time_constant);
-
- if (nm_tx_pending (ring) && !n_free_slots)
- {
- cur_ring++;
- continue;
- }
- }
-
- while (n_left && n_free_slots)
- {
- vlib_buffer_t *b0 = 0;
- u32 bi = buffers[0];
- u32 len;
- u32 offset = 0;
- buffers++;
-
- struct netmap_slot *slot = &ring->slot[cur];
-
- do
- {
- b0 = vlib_get_buffer (vm, bi);
- len = b0->current_length;
- /* memcpy */
- clib_memcpy_fast ((u8 *) NETMAP_BUF (ring, slot->buf_idx) +
- offset, vlib_buffer_get_current (b0), len);
- offset += len;
- }
- while ((bi = b0->next_buffer));
-
- slot->len = offset;
- cur = (cur + 1) % ring->num_slots;
- n_free_slots--;
- n_left--;
- }
- CLIB_MEMORY_BARRIER ();
- ring->head = ring->cur = cur;
- }
-
- if (n_left < frame->n_vectors)
- ioctl (nif->fd, NIOCTXSYNC, NULL);
-
- clib_spinlock_unlock_if_init (&nif->lockp);
-
- if (n_left)
- vlib_error_count (vm, node->node_index,
- (n_left ==
- frame->n_vectors ? NETMAP_TX_ERROR_PENDING_MSGS :
- NETMAP_TX_ERROR_NO_FREE_SLOTS), n_left);
-
- vlib_buffer_free (vm, vlib_frame_vector_args (frame), frame->n_vectors);
- return frame->n_vectors;
-}
-
-static void
-netmap_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
- u32 node_index)
-{
- netmap_main_t *apm = &netmap_main;
- vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
- netmap_if_t *nif = pool_elt_at_index (apm->interfaces, hw->dev_instance);
-
- /* Shut off redirection */
- if (node_index == ~0)
- {
- nif->per_interface_next_index = node_index;
- return;
- }
-
- nif->per_interface_next_index =
- vlib_node_add_next (vlib_get_main (), netmap_input_node.index,
- node_index);
-}
-
-static void
-netmap_clear_hw_interface_counters (u32 instance)
-{
- /* Nothing for now */
-}
-
-static clib_error_t *
-netmap_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
-{
- netmap_main_t *apm = &netmap_main;
- vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
- netmap_if_t *nif = pool_elt_at_index (apm->interfaces, hw->dev_instance);
- u32 hw_flags;
-
- nif->is_admin_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
-
- if (nif->is_admin_up)
- hw_flags = VNET_HW_INTERFACE_FLAG_LINK_UP;
- else
- hw_flags = 0;
-
- vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
-
- return 0;
-}
-
-static clib_error_t *
-netmap_subif_add_del_function (vnet_main_t * vnm,
- u32 hw_if_index,
- struct vnet_sw_interface_t *st, int is_add)
-{
- /* Nothing for now */
- return 0;
-}
-
-/* *INDENT-OFF* */
-VNET_DEVICE_CLASS (netmap_device_class) = {
- .name = "netmap",
- .format_device_name = format_netmap_device_name,
- .format_device = format_netmap_device,
- .format_tx_trace = format_netmap_tx_trace,
- .tx_function_n_errors = NETMAP_TX_N_ERROR,
- .tx_function_error_strings = netmap_tx_func_error_strings,
- .rx_redirect_to_node = netmap_set_interface_next_node,
- .clear_counters = netmap_clear_hw_interface_counters,
- .admin_up_down_function = netmap_interface_admin_up_down,
- .subif_add_del_function = netmap_subif_add_del_function,
-};
-/* *INDENT-ON* */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/extras/deprecated/netmap/net_netmap.h b/extras/deprecated/netmap/net_netmap.h
deleted file mode 100644
index fd4253b7c0c..00000000000
--- a/extras/deprecated/netmap/net_netmap.h
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``S IS''AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * $FreeBSD: head/sys/net/netmap.h 251139 2013-05-30 14:07:14Z luigi $
- *
- * Definitions of constants and the structures used by the netmap
- * framework, for the part visible to both kernel and userspace.
- * Detailed info on netmap is available with "man netmap" or at
- *
- * http://info.iet.unipi.it/~luigi/netmap/
- *
- * This API is also used to communicate with the VALE software switch
- */
-
-#ifndef _NET_NETMAP_H_
-#define _NET_NETMAP_H_
-
-#define NETMAP_API 11 /* current API version */
-
-#define NETMAP_MIN_API 11 /* 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
- * digging into OS headers to find the exact value we use an estimate
- * that should cover most architectures.
- */
-#define NM_CACHE_ALIGN 128
-
-/*
- * --- Netmap data structures ---
- *
- * The userspace data structures used by netmap are shown below.
- * They are allocated by the kernel and mmap()ed by userspace threads.
- * Pointers are implemented as memory offsets or indexes,
- * so that they can be easily dereferenced in kernel and userspace.
-
- KERNEL (opaque, obviously)
-
- ====================================================================
- |
- USERSPACE | struct netmap_ring
- +---->+---------------+
- / | head,cur,tail |
- struct netmap_if (nifp, 1 per fd) / | buf_ofs |
- +---------------+ / | other fields |
- | ni_tx_rings | / +===============+
- | ni_rx_rings | / | buf_idx, len | slot[0]
- | | / | flags, ptr |
- | | / +---------------+
- +===============+ / | buf_idx, len | slot[1]
- | txring_ofs[0] | (rel.to nifp)--' | flags, ptr |
- | txring_ofs[1] | +---------------+
- (tx+1 entries) (num_slots entries)
- | txring_ofs[t] | | buf_idx, len | slot[n-1]
- +---------------+ | flags, ptr |
- | rxring_ofs[0] | +---------------+
- | rxring_ofs[1] |
- (rx+1 entries)
- | rxring_ofs[r] |
- +---------------+
-
- * For each "interface" (NIC, host stack, PIPE, VALE switch port) bound to
- * a file descriptor, the mmap()ed region contains a (logically readonly)
- * struct netmap_if pointing to struct netmap_ring's.
- *
- * There is one netmap_ring per physical NIC ring, plus one tx/rx ring
- * pair attached to the host stack (this pair is unused for non-NIC ports).
- *
- * All physical/host stack ports share the same memory region,
- * so that zero-copy can be implemented between them.
- * VALE switch ports instead have separate memory regions.
- *
- * The netmap_ring is the userspace-visible replica of the NIC ring.
- * Each slot has the index of a buffer (MTU-sized and residing in the
- * mmapped region), its length and some flags. An extra 64-bit pointer
- * is provided for user-supplied buffers in the tx path.
- *
- * In user space, the buffer address is computed as
- * (char *)ring + buf_ofs + index * NETMAP_BUF_SIZE
- *
- * Added in NETMAP_API 11:
- *
- * + NIOCREGIF can request the allocation of extra spare buffers from
- * the same memory pool. The desired number of buffers must be in
- * nr_arg3. The ioctl may return fewer buffers, depending on memory
- * availability. nr_arg3 will return the actual value, and, once
- * mapped, nifp->ni_bufs_head will be the index of the first buffer.
- *
- * The buffers are linked to each other using the first uint32_t
- * as the index. On close, ni_bufs_head must point to the list of
- * buffers to be released.
- *
- * + NIOCREGIF can request space for extra rings (and buffers)
- * allocated in the same memory space. The number of extra rings
- * is in nr_arg1, and is advisory. This is a no-op on NICs where
- * the size of the memory space is fixed.
- *
- * + NIOCREGIF can attach to PIPE rings sharing the same memory
- * space with a parent device. The ifname indicates the parent device,
- * which must already exist. Flags in nr_flags indicate if we want to
- * bind the master or slave side, the index (from nr_ringid)
- * is just a cookie and does not need to be sequential.
- *
- * + NIOCREGIF can also attach to 'monitor' rings that replicate
- * the content of specific rings, also from the same memory space.
- *
- * Extra flags in nr_flags support the above functions.
- * Application libraries may use the following naming scheme:
- * netmap:foo all NIC ring pairs
- * netmap:foo^ only host ring pair
- * netmap:foo+ all NIC ring + host ring pairs
- * netmap:foo-k the k-th NIC ring pair
- * netmap:foo{k PIPE ring pair k, master side
- * netmap:foo}k PIPE ring pair k, slave side
- */
-
-/*
- * struct netmap_slot is a buffer descriptor
- */
-struct netmap_slot {
- uint32_t buf_idx; /* buffer index */
- uint16_t len; /* length for this slot */
- uint16_t flags; /* buf changed, etc. */
- uint64_t ptr; /* pointer for indirect buffers */
-};
-
-/*
- * The following flags control how the slot is used
- */
-
-#define NS_BUF_CHANGED 0x0001 /* buf_idx changed */
- /*
- * must be set whenever buf_idx is changed (as it might be
- * necessary to recompute the physical address and mapping)
- *
- * It is also set by the kernel whenever the buf_idx is
- * changed internally (e.g., by pipes). Applications may
- * use this information to know when they can reuse the
- * contents of previously prepared buffers.
- */
-
-#define NS_REPORT 0x0002 /* ask the hardware to report results */
- /*
- * Request notification when slot is used by the hardware.
- * Normally transmit completions are handled lazily and
- * may be unreported. This flag lets us know when a slot
- * has been sent (e.g. to terminate the sender).
- */
-
-#define NS_FORWARD 0x0004 /* pass packet 'forward' */
- /*
- * (Only for physical ports, rx rings with NR_FORWARD set).
- * Slot released to the kernel (i.e. before ring->head) with
- * this flag set are passed to the peer ring (host/NIC),
- * thus restoring the host-NIC connection for these slots.
- * This supports efficient traffic monitoring or firewalling.
- */
-
-#define NS_NO_LEARN 0x0008 /* disable bridge learning */
- /*
- * On a VALE switch, do not 'learn' the source port for
- * this buffer.
- */
-
-#define NS_INDIRECT 0x0010 /* userspace buffer */
- /*
- * (VALE tx rings only) data is in a userspace buffer,
- * whose address is in the 'ptr' field in the slot.
- */
-
-#define NS_MOREFRAG 0x0020 /* packet has more fragments */
- /*
- * (VALE ports only)
- * Set on all but the last slot of a multi-segment packet.
- * The 'len' field refers to the individual fragment.
- */
-
-#define NS_PORT_SHIFT 8
-#define NS_PORT_MASK (0xff << NS_PORT_SHIFT)
- /*
- * The high 8 bits of the flag, if not zero, indicate the
- * destination port for the VALE switch, overriding
- * the lookup table.
- */
-
-#define NS_RFRAGS(_slot) ( ((_slot)->flags >> 8) & 0xff)
- /*
- * (VALE rx rings only) the high 8 bits
- * are the number of fragments.
- */
-
-
-/*
- * struct netmap_ring
- *
- * Netmap representation of a TX or RX ring (also known as "queue").
- * This is a queue implemented as a fixed-size circular array.
- * At the software level the important fields are: head, cur, tail.
- *
- * In TX rings:
- *
- * head first slot available for transmission.
- * cur wakeup point. select() and poll() will unblock
- * when 'tail' moves past 'cur'
- * tail (readonly) first slot reserved to the kernel
- *
- * [head .. tail-1] can be used for new packets to send;
- * 'head' and 'cur' must be incremented as slots are filled
- * with new packets to be sent;
- * 'cur' can be moved further ahead if we need more space
- * for new transmissions. XXX todo (2014-03-12)
- *
- * In RX rings:
- *
- * head first valid received packet
- * cur wakeup point. select() and poll() will unblock
- * when 'tail' moves past 'cur'
- * tail (readonly) first slot reserved to the kernel
- *
- * [head .. tail-1] contain received packets;
- * 'head' and 'cur' must be incremented as slots are consumed
- * and can be returned to the kernel;
- * 'cur' can be moved further ahead if we want to wait for
- * new packets without returning the previous ones.
- *
- * DATA OWNERSHIP/LOCKING:
- * The netmap_ring, and all slots and buffers in the range
- * [head .. tail-1] are owned by the user program;
- * the kernel only accesses them during a netmap system call
- * and in the user thread context.
- *
- * Other slots and buffers are reserved for use by the kernel
- */
-struct netmap_ring {
- /*
- * buf_ofs is meant to be used through macros.
- * It contains the offset of the buffer region from this
- * descriptor.
- */
- const int64_t buf_ofs;
- const uint32_t num_slots; /* number of slots in the ring. */
- const uint32_t nr_buf_size;
- const uint16_t ringid;
- const uint16_t dir; /* 0: tx, 1: rx */
-
- uint32_t head; /* (u) first user slot */
- uint32_t cur; /* (u) wakeup point */
- uint32_t tail; /* (k) first kernel slot */
-
- uint32_t flags;
-
- struct timeval ts; /* (k) time of last *sync() */
-
- /* opaque room for a mutex or similar object */
-#if !defined(_WIN32) || defined(__CYGWIN__)
- uint8_t __attribute__((__aligned__(NM_CACHE_ALIGN))) sem[128];
-#else
- uint8_t __declspec(align(NM_CACHE_ALIGN)) sem[128];
-#endif
-
- /* the slots follow. This struct has variable size */
- struct netmap_slot slot[0]; /* array of slots. */
-};
-
-
-/*
- * RING FLAGS
- */
-#define NR_TIMESTAMP 0x0002 /* set timestamp on *sync() */
- /*
- * updates the 'ts' field on each netmap syscall. This saves
- * saves a separate gettimeofday(), and is not much worse than
- * software timestamps generated in the interrupt handler.
- */
-
-#define NR_FORWARD 0x0004 /* enable NS_FORWARD for ring */
- /*
- * Enables the NS_FORWARD slot flag for the ring.
- */
-
-
-/*
- * Netmap representation of an interface and its queue(s).
- * This is initialized by the kernel when binding a file
- * descriptor to a port, and should be considered as readonly
- * by user programs. The kernel never uses it.
- *
- * There is one netmap_if for each file descriptor on which we want
- * to select/poll.
- * select/poll operates on one or all pairs depending on the value of
- * nmr_queueid passed on the ioctl.
- */
-struct netmap_if {
- char ni_name[IFNAMSIZ]; /* name of the interface. */
- const uint32_t ni_version; /* API version, currently unused */
- const uint32_t ni_flags; /* properties */
-#define NI_PRIV_MEM 0x1 /* private memory region */
-
- /*
- * The number of packet rings available in netmap mode.
- * Physical NICs can have different numbers of tx and rx rings.
- * Physical NICs also have a 'host' ring pair.
- * Additionally, clients can request additional ring pairs to
- * be used for internal communication.
- */
- const uint32_t ni_tx_rings; /* number of HW tx rings */
- const uint32_t ni_rx_rings; /* number of HW rx rings */
-
- uint32_t ni_bufs_head; /* head index for extra bufs */
- uint32_t ni_spare1[5];
- /*
- * The following array contains the offset of each netmap ring
- * from this structure, in the following order:
- * NIC tx rings (ni_tx_rings); host tx ring (1); extra tx rings;
- * NIC rx rings (ni_rx_rings); host tx ring (1); extra rx rings.
- *
- * The area is filled up by the kernel on NIOCREGIF,
- * and then only read by userspace code.
- */
- const ssize_t ring_ofs[0];
-};
-
-
-#ifndef NIOCREGIF
-/*
- * ioctl names and related fields
- *
- * NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues,
- * whose identity is set in NIOCREGIF through nr_ringid.
- * These are non blocking and take no argument.
- *
- * NIOCGINFO takes a struct ifreq, the interface name is the input,
- * the outputs are number of queues and number of descriptor
- * for each queue (useful to set number of threads etc.).
- * The info returned is only advisory and may change before
- * the interface is bound to a file descriptor.
- *
- * NIOCREGIF takes an interface name within a struct nmre,
- * and activates netmap mode on the interface (if possible).
- *
- * The argument to NIOCGINFO/NIOCREGIF overlays struct ifreq so we
- * can pass it down to other NIC-related ioctls.
- *
- * The actual argument (struct nmreq) has a number of options to request
- * different functions.
- * The following are used in NIOCREGIF when nr_cmd == 0:
- *
- * nr_name (in)
- * The name of the port (em0, valeXXX:YYY, etc.)
- * limited to IFNAMSIZ for backward compatibility.
- *
- * nr_version (in/out)
- * Must match NETMAP_API as used in the kernel, error otherwise.
- * Always returns the desired value on output.
- *
- * nr_tx_slots, nr_tx_slots, nr_tx_rings, nr_rx_rings (in/out)
- * On input, non-zero values may be used to reconfigure the port
- * according to the requested values, but this is not guaranteed.
- * On output the actual values in use are reported.
- *
- * nr_ringid (in)
- * Indicates how rings should be bound to the file descriptors.
- * If nr_flags != 0, then the low bits (in NETMAP_RING_MASK)
- * are used to indicate the ring number, and nr_flags specifies
- * the actual rings to bind. NETMAP_NO_TX_POLL is unaffected.
- *
- * NOTE: THE FOLLOWING (nr_flags == 0) IS DEPRECATED:
- * If nr_flags == 0, NETMAP_HW_RING and NETMAP_SW_RING control
- * the binding as follows:
- * 0 (default) binds all physical rings
- * NETMAP_HW_RING | ring number binds a single ring pair
- * NETMAP_SW_RING binds only the host tx/rx rings
- *
- * NETMAP_NO_TX_POLL can be OR-ed to make select()/poll() push
- * packets on tx rings only if POLLOUT is set.
- * The default is to push any pending packet.
- *
- * NETMAP_DO_RX_POLL can be OR-ed to make select()/poll() release
- * packets on rx rings also when POLLIN is NOT set.
- * The default is to touch the rx ring only with POLLIN.
- * Note that this is the opposite of TX because it
- * reflects the common usage.
- *
- * NOTE: NETMAP_PRIV_MEM IS DEPRECATED, use nr_arg2 instead.
- * NETMAP_PRIV_MEM is set on return for ports that do not use
- * the global memory allocator.
- * This information is not significant and applications
- * should look at the region id in nr_arg2
- *
- * nr_flags is the recommended mode to indicate which rings should
- * be bound to a file descriptor. Values are NR_REG_*
- *
- * nr_arg1 (in) The number of extra rings to be reserved.
- * Especially when allocating a VALE port the system only
- * allocates the amount of memory needed for the port.
- * If more shared memory rings are desired (e.g. for pipes),
- * the first invocation for the same basename/allocator
- * should specify a suitable number. Memory cannot be
- * extended after the first allocation without closing
- * all ports on the same region.
- *
- * nr_arg2 (in/out) The identity of the memory region used.
- * On input, 0 means the system decides autonomously,
- * other values may try to select a specific region.
- * On return the actual value is reported.
- * Region '1' is the global allocator, normally shared
- * by all interfaces. Other values are private regions.
- * If two ports the same region zero-copy is possible.
- *
- * nr_arg3 (in/out) number of extra buffers to be allocated.
- *
- *
- *
- * nr_cmd (in) if non-zero indicates a special command:
- * NETMAP_BDG_ATTACH and nr_name = vale*:ifname
- * attaches the NIC to the switch; nr_ringid specifies
- * which rings to use. Used by vale-ctl -a ...
- * nr_arg1 = NETMAP_BDG_HOST also attaches the host port
- * as in vale-ctl -h ...
- *
- * NETMAP_BDG_DETACH and nr_name = vale*:ifname
- * disconnects a previously attached NIC.
- * Used by vale-ctl -d ...
- *
- * NETMAP_BDG_LIST
- * list the configuration of VALE switches.
- *
- * NETMAP_BDG_VNET_HDR
- * Set the virtio-net header length used by the client
- * of a VALE switch port.
- *
- * NETMAP_BDG_NEWIF
- * create a persistent VALE port with name nr_name.
- * Used by vale-ctl -n ...
- *
- * NETMAP_BDG_DELIF
- * delete a persistent VALE port. Used by vale-ctl -d ...
- *
- * nr_arg1, nr_arg2, nr_arg3 (in/out) command specific
- *
- *
- *
- */
-
-
-/*
- * struct nmreq overlays a struct ifreq (just the name)
- */
-struct nmreq {
- char nr_name[IFNAMSIZ];
- uint32_t nr_version; /* API version */
- uint32_t nr_offset; /* nifp offset in the shared region */
- uint32_t nr_memsize; /* size of the shared region */
- uint32_t nr_tx_slots; /* slots in tx rings */
- uint32_t nr_rx_slots; /* slots in rx rings */
- uint16_t nr_tx_rings; /* number of tx rings */
- uint16_t nr_rx_rings; /* number of rx rings */
-
- uint16_t nr_ringid; /* ring(s) we care about */
-#define NETMAP_HW_RING 0x4000 /* single NIC ring pair */
-#define NETMAP_SW_RING 0x2000 /* only host ring pair */
-
-#define NETMAP_RING_MASK 0x0fff /* the ring number */
-
-#define NETMAP_NO_TX_POLL 0x1000 /* no automatic txsync on poll */
-
-#define NETMAP_DO_RX_POLL 0x8000 /* DO automatic rxsync on poll */
-
- uint16_t nr_cmd;
-#define NETMAP_BDG_ATTACH 1 /* attach the NIC */
-#define NETMAP_BDG_DETACH 2 /* detach the NIC */
-#define NETMAP_BDG_REGOPS 3 /* register bridge callbacks */
-#define NETMAP_BDG_LIST 4 /* get bridge's info */
-#define NETMAP_BDG_VNET_HDR 5 /* set the port virtio-net-hdr length */
-#define NETMAP_BDG_OFFSET NETMAP_BDG_VNET_HDR /* deprecated alias */
-#define NETMAP_BDG_NEWIF 6 /* create a virtual port */
-#define NETMAP_BDG_DELIF 7 /* destroy a virtual port */
-#define NETMAP_PT_HOST_CREATE 8 /* create ptnetmap kthreads */
-#define NETMAP_PT_HOST_DELETE 9 /* delete ptnetmap kthreads */
-#define NETMAP_BDG_POLLING_ON 10 /* delete polling kthread */
-#define NETMAP_BDG_POLLING_OFF 11 /* delete polling kthread */
-#define NETMAP_VNET_HDR_GET 12 /* get the port virtio-net-hdr length */
- uint16_t nr_arg1; /* reserve extra rings in NIOCREGIF */
-#define NETMAP_BDG_HOST 1 /* attach the host stack on ATTACH */
-
- uint16_t nr_arg2;
- uint32_t nr_arg3; /* req. extra buffers in NIOCREGIF */
- uint32_t nr_flags;
- /* various modes, extends nr_ringid */
- uint32_t spare2[1];
-};
-
-#define NR_REG_MASK 0xf /* values for nr_flags */
-enum { NR_REG_DEFAULT = 0, /* backward compat, should not be used. */
- NR_REG_ALL_NIC = 1,
- NR_REG_SW = 2,
- NR_REG_NIC_SW = 3,
- NR_REG_ONE_NIC = 4,
- NR_REG_PIPE_MASTER = 5,
- NR_REG_PIPE_SLAVE = 6,
-};
-/* monitor uses the NR_REG to select the rings to monitor */
-#define NR_MONITOR_TX 0x100
-#define NR_MONITOR_RX 0x200
-#define NR_ZCOPY_MON 0x400
-/* request exclusive access to the selected rings */
-#define NR_EXCLUSIVE 0x800
-/* request ptnetmap host support */
-#define NR_PASSTHROUGH_HOST NR_PTNETMAP_HOST /* deprecated */
-#define NR_PTNETMAP_HOST 0x1000
-#define NR_RX_RINGS_ONLY 0x2000
-#define NR_TX_RINGS_ONLY 0x4000
-/* Applications set this flag if they are able to deal with virtio-net headers,
- * that is send/receive frames that start with a virtio-net header.
- * If not set, NIOCREGIF will fail with netmap ports that require applications
- * to use those headers. If the flag is set, the application can use the
- * NETMAP_VNET_HDR_GET command to figure out the header length. */
-#define NR_ACCEPT_VNET_HDR 0x8000
-
-
-/*
- * Windows does not have _IOWR(). _IO(), _IOW() and _IOR() are defined
- * in ws2def.h but not sure if they are in the form we need.
- * XXX so we redefine them
- * in a convenient way to use for DeviceIoControl signatures
- */
-#ifdef _WIN32
-#undef _IO // ws2def.h
-#define _WIN_NM_IOCTL_TYPE 40000
-#define _IO(_c, _n) CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \
- METHOD_BUFFERED, FILE_ANY_ACCESS )
-#define _IO_direct(_c, _n) CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \
- METHOD_OUT_DIRECT, FILE_ANY_ACCESS )
-
-#define _IOWR(_c, _n, _s) _IO(_c, _n)
-
-/* We havesome internal sysctl in addition to the externally visible ones */
-#define NETMAP_MMAP _IO_direct('i', 160) // note METHOD_OUT_DIRECT
-#define NETMAP_POLL _IO('i', 162)
-
-/* and also two setsockopt for sysctl emulation */
-#define NETMAP_SETSOCKOPT _IO('i', 140)
-#define NETMAP_GETSOCKOPT _IO('i', 141)
-
-
-//These linknames are for the Netmap Core Driver
-#define NETMAP_NT_DEVICE_NAME L"\\Device\\NETMAP"
-#define NETMAP_DOS_DEVICE_NAME L"\\DosDevices\\netmap"
-
-//Definition of a structure used to pass a virtual address within an IOCTL
-typedef struct _MEMORY_ENTRY {
- PVOID pUsermodeVirtualAddress;
-} MEMORY_ENTRY, *PMEMORY_ENTRY;
-
-typedef struct _POLL_REQUEST_DATA {
- int events;
- int timeout;
- int revents;
-} POLL_REQUEST_DATA;
-
-#endif /* _WIN32 */
-
-/*
- * FreeBSD uses the size value embedded in the _IOWR to determine
- * how much to copy in/out. So we need it to match the actual
- * data structure we pass. We put some spares in the structure
- * to ease compatibility with other versions
- */
-#define NIOCGINFO _IOWR('i', 145, struct nmreq) /* return IF info */
-#define NIOCREGIF _IOWR('i', 146, struct nmreq) /* interface register */
-#define NIOCTXSYNC _IO('i', 148) /* sync tx queues */
-#define NIOCRXSYNC _IO('i', 149) /* sync rx queues */
-#define NIOCCONFIG _IOWR('i',150, struct nm_ifreq) /* for ext. modules */
-#endif /* !NIOCREGIF */
-
-
-/*
- * Helper functions for kernel and userspace
- */
-
-/*
- * check if space is available in the ring.
- */
-static inline int
-nm_ring_empty(struct netmap_ring *ring)
-{
- return (ring->cur == ring->tail);
-}
-
-/*
- * Opaque structure that is passed to an external kernel
- * module via ioctl(fd, NIOCCONFIG, req) for a user-owned
- * bridge port (at this point ephemeral VALE interface).
- */
-#define NM_IFRDATA_LEN 256
-struct nm_ifreq {
- char nifr_name[IFNAMSIZ];
- char data[NM_IFRDATA_LEN];
-};
-
-/*
- * netmap kernel thread configuration
- */
-/* bhyve/vmm.ko MSIX parameters for IOCTL */
-struct ptn_vmm_ioctl_msix {
- uint64_t msg;
- uint64_t addr;
-};
-
-/* IOCTL parameters */
-struct nm_kth_ioctl {
- u_long com;
- /* TODO: use union */
- union {
- struct ptn_vmm_ioctl_msix msix;
- } data;
-};
-
-/* Configuration of a ptnetmap ring */
-struct ptnet_ring_cfg {
- uint64_t ioeventfd; /* eventfd in linux, tsleep() parameter in FreeBSD */
- uint64_t irqfd; /* eventfd in linux, ioctl fd in FreeBSD */
- struct nm_kth_ioctl ioctl; /* ioctl parameter to send irq (only used in bhyve/FreeBSD) */
-};
-#endif /* _NET_NETMAP_H_ */
diff --git a/extras/deprecated/netmap/netmap.c b/extras/deprecated/netmap/netmap.c
deleted file mode 100644
index 7cab6bb0289..00000000000
--- a/extras/deprecated/netmap/netmap.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2016 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/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;
-
-static u32
-netmap_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi,
- u32 flags)
-{
- /* nothing for now */
- return 0;
-}
-
-static clib_error_t *
-netmap_fd_read_ready (clib_file_t * uf)
-{
- vlib_main_t *vm = vlib_get_main ();
- netmap_main_t *nm = &netmap_main;
- u32 idx = uf->private_data;
-
- nm->pending_input_bitmap =
- clib_bitmap_set (nm->pending_input_bitmap, idx, 1);
-
- /* Schedule the rx node */
- vlib_node_set_interrupt_pending (vm, netmap_input_node.index);
-
- return 0;
-}
-
-static void
-close_netmap_if (netmap_main_t * nm, netmap_if_t * nif)
-{
- if (nif->clib_file_index != ~0)
- {
- clib_file_del (&file_main, file_main.file_pool + nif->clib_file_index);
- nif->clib_file_index = ~0;
- }
- else if (nif->fd > -1)
- close (nif->fd);
-
- if (nif->mem_region)
- {
- netmap_mem_region_t *reg = &nm->mem_regions[nif->mem_region];
- if (--reg->refcnt == 0)
- {
- munmap (reg->mem, reg->region_size);
- reg->region_size = 0;
- }
- }
-
-
- mhash_unset (&nm->if_index_by_host_if_name, nif->host_if_name,
- &nif->if_index);
- vec_free (nif->host_if_name);
- vec_free (nif->req);
-
- clib_memset (nif, 0, sizeof (*nif));
- pool_put (nm->interfaces, nif);
-}
-
-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);
- }));
-
- return 0;
-}
-
-int
-netmap_worker_thread_disable ()
-{
- foreach_vlib_main ((
- {
- vlib_node_set_state (this_vlib_main,
- netmap_input_node.index,
- VLIB_NODE_STATE_INTERRUPT);
- }));
-
- return 0;
-}
-
-int
-netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set,
- u8 is_pipe, u8 is_master, u32 * sw_if_index)
-{
- netmap_main_t *nm = &netmap_main;
- int ret = 0;
- 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;
- struct nmreq *req = 0;
- netmap_mem_region_t *reg;
- vlib_thread_main_t *tm = vlib_get_thread_main ();
- int fd;
-
- p = mhash_get (&nm->if_index_by_host_if_name, if_name);
- if (p)
- return VNET_API_ERROR_SUBIF_ALREADY_EXISTS;
-
- fd = open ("/dev/netmap", O_RDWR);
- if (fd < 0)
- return VNET_API_ERROR_SUBIF_ALREADY_EXISTS;
-
- pool_get (nm->interfaces, nif);
- nif->if_index = nif - nm->interfaces;
- nif->fd = fd;
- nif->clib_file_index = ~0;
-
- vec_validate (req, 0);
- nif->req = req;
- req->nr_version = NETMAP_API;
- req->nr_flags = NR_REG_ALL_NIC;
-
- if (is_pipe)
- req->nr_flags = is_master ? NR_REG_PIPE_MASTER : NR_REG_PIPE_SLAVE;
- else
- req->nr_flags = NR_REG_ALL_NIC;
-
- req->nr_flags |= NR_ACCEPT_VNET_HDR;
- snprintf (req->nr_name, IFNAMSIZ, "%s", if_name);
- req->nr_name[IFNAMSIZ - 1] = 0;
-
- if (ioctl (nif->fd, NIOCREGIF, req))
- {
- ret = VNET_API_ERROR_NOT_CONNECTED;
- goto error;
- }
-
- nif->mem_region = req->nr_arg2;
- vec_validate (nm->mem_regions, nif->mem_region);
- reg = &nm->mem_regions[nif->mem_region];
- if (reg->region_size == 0)
- {
- reg->mem = mmap (NULL, req->nr_memsize, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- clib_warning ("mem %p", reg->mem);
- if (reg->mem == MAP_FAILED)
- {
- ret = VNET_API_ERROR_NOT_CONNECTED;
- goto error;
- }
- reg->region_size = req->nr_memsize;
- }
- 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;
- nif->host_if_name = if_name;
- nif->per_interface_next_index = ~0;
-
- if (tm->n_vlib_mains > 1)
- clib_spinlock_init (&nif->lockp);
-
- {
- clib_file_t template = { 0 };
- template.read_function = netmap_fd_read_ready;
- template.file_descriptor = nif->fd;
- template.private_data = nif->if_index;
- template.description = format (0, "netmap socket");
- nif->clib_file_index = clib_file_add (&file_main, &template);
- }
-
- /*use configured or generate random MAC address */
- if (hw_addr_set)
- memcpy (hw_addr, hw_addr_set, 6);
- else
- {
- f64 now = vlib_time_now (vm);
- u32 rnd;
- rnd = (u32) (now * 1e6);
- rnd = random_u32 (&rnd);
-
- memcpy (hw_addr + 2, &rnd, sizeof (rnd));
- hw_addr[0] = 2;
- 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);
-
- if (error)
- {
- clib_error_report (error);
- ret = VNET_API_ERROR_SYSCALL_ERROR_1;
- goto error;
- }
-
- sw = vnet_get_hw_sw_interface (vnm, nif->hw_if_index);
- nif->sw_if_index = sw->sw_if_index;
-
- mhash_set_mem (&nm->if_index_by_host_if_name, if_name, &nif->if_index, 0);
-
- if (sw_if_index)
- *sw_if_index = nif->sw_if_index;
-
- if (tm->n_vlib_mains > 1 && pool_elts (nm->interfaces) == 1)
- netmap_worker_thread_enable ();
-
- return 0;
-
-error:
- close_netmap_if (nm, nif);
- return ret;
-}
-
-int
-netmap_delete_if (vlib_main_t * vm, u8 * host_if_name)
-{
- vnet_main_t *vnm = vnet_get_main ();
- netmap_main_t *nm = &netmap_main;
- netmap_if_t *nif;
- uword *p;
- vlib_thread_main_t *tm = vlib_get_thread_main ();
-
- p = mhash_get (&nm->if_index_by_host_if_name, host_if_name);
- if (p == NULL)
- {
- clib_warning ("Host interface %s does not exist", host_if_name);
- return VNET_API_ERROR_SYSCALL_ERROR_1;
- }
- nif = pool_elt_at_index (nm->interfaces, p[0]);
-
- /* bring down the interface */
- vnet_hw_interface_set_flags (vnm, nif->hw_if_index, 0);
-
- ethernet_delete_interface (vnm, nif->hw_if_index);
-
- close_netmap_if (nm, nif);
-
- if (tm->n_vlib_mains > 1 && pool_elts (nm->interfaces) == 0)
- netmap_worker_thread_disable ();
-
- return 0;
-}
-
-static clib_error_t *
-netmap_init (vlib_main_t * vm)
-{
- netmap_main_t *nm = &netmap_main;
- vlib_thread_main_t *tm = vlib_get_thread_main ();
- vlib_thread_registration_t *tr;
- uword *p;
-
- clib_memset (nm, 0, sizeof (netmap_main_t));
-
- nm->input_cpu_first_index = 0;
- nm->input_cpu_count = 1;
-
- /* find out which cpus will be used for input */
- p = hash_get_mem (tm->thread_registrations_by_name, "workers");
- tr = p ? (vlib_thread_registration_t *) p[0] : 0;
-
- if (tr && tr->count > 0)
- {
- nm->input_cpu_first_index = tr->first_index;
- nm->input_cpu_count = tr->count;
- }
-
- mhash_init_vec_string (&nm->if_index_by_host_if_name, sizeof (uword));
-
- vec_validate_aligned (nm->rx_buffers, tm->n_vlib_mains - 1,
- CLIB_CACHE_LINE_BYTES);
-
- return 0;
-}
-
-VLIB_INIT_FUNCTION (netmap_init);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/extras/deprecated/netmap/netmap.h b/extras/deprecated/netmap/netmap.h
deleted file mode 100644
index 29f855fda8e..00000000000
--- a/extras/deprecated/netmap/netmap.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2016 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) 2011-2014 Universita` di Pisa. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <vppinfra/lock.h>
-
-typedef struct
-{
- CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- clib_spinlock_t lockp;
- u8 *host_if_name;
- uword if_index;
- u32 hw_if_index;
- u32 sw_if_index;
- u32 clib_file_index;
-
- u32 per_interface_next_index;
- u8 is_admin_up;
-
- /* netmap */
- struct nmreq *req;
- u16 mem_region;
- int fd;
- struct netmap_if *nifp;
- u16 first_tx_ring;
- u16 last_tx_ring;
- u16 first_rx_ring;
- u16 last_rx_ring;
-
-} netmap_if_t;
-
-typedef struct
-{
- char *mem;
- u32 region_size;
- int refcnt;
-} netmap_mem_region_t;
-
-typedef struct
-{
- CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- netmap_if_t *interfaces;
-
- /* bitmap of pending rx interfaces */
- uword *pending_input_bitmap;
-
- /* rx buffer cache */
- u32 **rx_buffers;
-
- /* hash of host interface names */
- mhash_t if_index_by_host_if_name;
-
- /* vector of memory regions */
- netmap_mem_region_t *mem_regions;
-
- /* first cpu index */
- u32 input_cpu_first_index;
-
- /* total cpu count */
- u32 input_cpu_count;
-} netmap_main_t;
-
-extern netmap_main_t netmap_main;
-extern vnet_device_class_t netmap_device_class;
-extern vlib_node_registration_t netmap_input_node;
-
-int netmap_create_if (vlib_main_t * vm, u8 * host_if_name, u8 * hw_addr_set,
- u8 is_pipe, u8 is_master, u32 * sw_if_index);
-int netmap_delete_if (vlib_main_t * vm, u8 * host_if_name);
-
-
-/* Macros and helper functions from sys/net/netmap_user.h */
-
-#ifdef _NET_NETMAP_H_
-
-#define _NETMAP_OFFSET(type, ptr, offset) \
- ((type)(void *)((char *)(ptr) + (offset)))
-
-#define NETMAP_IF(_base, _ofs) _NETMAP_OFFSET(struct netmap_if *, _base, _ofs)
-
-#define NETMAP_TXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \
- nifp, (nifp)->ring_ofs[index] )
-
-#define NETMAP_RXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \
- nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] )
-
-#define NETMAP_BUF(ring, index) \
- ((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size))
-
-#define NETMAP_BUF_IDX(ring, buf) \
- ( ((char *)(buf) - ((char *)(ring) + (ring)->buf_ofs) ) / \
- (ring)->nr_buf_size )
-
-static inline uint32_t
-nm_ring_next (struct netmap_ring *ring, uint32_t i)
-{
- return (PREDICT_FALSE (i + 1 == ring->num_slots) ? 0 : i + 1);
-}
-
-
-/*
- * Return 1 if we have pending transmissions in the tx ring.
- * When everything is complete ring->head = ring->tail + 1 (modulo ring size)
- */
-static inline int
-nm_tx_pending (struct netmap_ring *ring)
-{
- return nm_ring_next (ring, ring->tail) != ring->head;
-}
-
-static inline uint32_t
-nm_ring_space (struct netmap_ring *ring)
-{
- int ret = ring->tail - ring->cur;
- if (ret < 0)
- ret += ring->num_slots;
- return ret;
-}
-#endif
-
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/extras/deprecated/netmap/netmap_api.c b/extras/deprecated/netmap/netmap_api.c
deleted file mode 100644
index ee05ec22d25..00000000000
--- a/extras/deprecated/netmap/netmap_api.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *------------------------------------------------------------------
- * netmap_api.c - netmap api
- *
- * Copyright (c) 2016 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 <vlibmemory/api.h>
-
-#include <vnet/interface.h>
-#include <vnet/api_errno.h>
-#include <vnet/devices/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 <vlibapi/api_helper_macros.h>
-
-#define foreach_vpe_api_msg \
-_(NETMAP_CREATE, netmap_create) \
-_(NETMAP_DELETE, netmap_delete) \
-
-static void
-vl_api_netmap_create_t_handler (vl_api_netmap_create_t * mp)
-{
- vlib_main_t *vm = vlib_get_main ();
- vl_api_netmap_create_reply_t *rmp;
- int rv = 0;
- u8 *if_name = NULL;
-
- if_name = format (0, "%s", mp->netmap_if_name);
- vec_add1 (if_name, 0);
-
- rv =
- netmap_create_if (vm, if_name, mp->use_random_hw_addr ? 0 : mp->hw_addr,
- mp->is_pipe, mp->is_master, 0);
-
- vec_free (if_name);
-
- REPLY_MACRO (VL_API_NETMAP_CREATE_REPLY);
-}
-
-static void
-vl_api_netmap_delete_t_handler (vl_api_netmap_delete_t * mp)
-{
- vlib_main_t *vm = vlib_get_main ();
- vl_api_netmap_delete_reply_t *rmp;
- int rv = 0;
- u8 *if_name = NULL;
-
- if_name = format (0, "%s", mp->netmap_if_name);
- vec_add1 (if_name, 0);
-
- rv = netmap_delete_if (vm, if_name);
-
- vec_free (if_name);
-
- 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 _
-}
-
-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);
-
- return 0;
-}
-
-VLIB_API_INIT_FUNCTION (netmap_api_hookup);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/extras/deprecated/netmap/node.c b/extras/deprecated/netmap/node.c
deleted file mode 100644
index b0a68824b9c..00000000000
--- a/extras/deprecated/netmap/node.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2016 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 <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vnet/ethernet/ethernet.h>
-#include <vnet/devices/devices.h>
-#include <vnet/feature/feature.h>
-
-#include <vnet/devices/netmap/net_netmap.h>
-#include <vnet/devices/netmap/netmap.h>
-
-#define foreach_netmap_input_error
-
-typedef enum
-{
-#define _(f,s) NETMAP_INPUT_ERROR_##f,
- foreach_netmap_input_error
-#undef _
- NETMAP_INPUT_N_ERROR,
-} netmap_input_error_t;
-
-static char *netmap_input_error_strings[] = {
-#define _(n,s) s,
- foreach_netmap_input_error
-#undef _
-};
-
-typedef struct
-{
- u32 next_index;
- u32 hw_if_index;
- struct netmap_slot slot;
-} netmap_input_trace_t;
-
-static u8 *
-format_netmap_input_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 *);
- netmap_input_trace_t *t = va_arg (*args, netmap_input_trace_t *);
- u32 indent = format_get_indent (s);
-
- s = format (s, "netmap: hw_if_index %d next-index %d",
- t->hw_if_index, t->next_index);
- s = format (s, "\n%Uslot: flags 0x%x len %u buf_idx %u",
- format_white_space, indent + 2,
- t->slot.flags, t->slot.len, t->slot.buf_idx);
- return s;
-}
-
-always_inline void
-buffer_add_to_chain (vlib_main_t * vm, u32 bi, u32 first_bi, u32 prev_bi)
-{
- vlib_buffer_t *b = vlib_get_buffer (vm, bi);
- vlib_buffer_t *first_b = vlib_get_buffer (vm, first_bi);
- vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_bi);
-
- /* update first buffer */
- first_b->total_length_not_including_first_buffer += b->current_length;
-
- /* update previous buffer */
- prev_b->next_buffer = bi;
- prev_b->flags |= VLIB_BUFFER_NEXT_PRESENT;
-
- /* update current buffer */
- b->next_buffer = 0;
-}
-
-always_inline uword
-netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame, netmap_if_t * nif)
-{
- u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
- uword n_trace = vlib_get_trace_count (vm, node);
- netmap_main_t *nm = &netmap_main;
- u32 n_rx_packets = 0;
- u32 n_rx_bytes = 0;
- u32 *to_next = 0;
- u32 n_free_bufs;
- struct netmap_ring *ring;
- int cur_ring;
- u32 thread_index = vm->thread_index;
- u32 n_buffer_bytes = vlib_buffer_get_default_data_size (vm);
-
- if (nif->per_interface_next_index != ~0)
- next_index = nif->per_interface_next_index;
-
- n_free_bufs = vec_len (nm->rx_buffers[thread_index]);
- if (PREDICT_FALSE (n_free_bufs < VLIB_FRAME_SIZE))
- {
- vec_validate (nm->rx_buffers[thread_index],
- VLIB_FRAME_SIZE + n_free_bufs - 1);
- 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;
- }
-
- cur_ring = nif->first_rx_ring;
- while (cur_ring <= nif->last_rx_ring && n_free_bufs)
- {
- int r = 0;
- u32 cur_slot_index;
- ring = NETMAP_RXRING (nif->nifp, cur_ring);
- r = nm_ring_space (ring);
-
- if (!r)
- {
- cur_ring++;
- continue;
- }
-
- if (r > n_free_bufs)
- r = n_free_bufs;
-
- cur_slot_index = ring->cur;
- while (r)
- {
- u32 n_left_to_next;
- u32 next0 = next_index;
- vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-
- while (r && n_left_to_next)
- {
- vlib_buffer_t *first_b0 = 0;
- u32 offset = 0;
- u32 bi0 = 0, first_bi0 = 0, prev_bi0;
- u32 next_slot_index = (cur_slot_index + 1) % ring->num_slots;
- u32 next2_slot_index = (cur_slot_index + 2) % ring->num_slots;
- struct netmap_slot *slot = &ring->slot[cur_slot_index];
- u32 data_len = slot->len;
-
- /* prefetch 2 slots in advance */
- CLIB_PREFETCH (&ring->slot[next2_slot_index],
- CLIB_CACHE_LINE_BYTES, LOAD);
- /* prefetch start of next packet */
- CLIB_PREFETCH (NETMAP_BUF
- (ring, ring->slot[next_slot_index].buf_idx),
- CLIB_CACHE_LINE_BYTES, LOAD);
-
- while (data_len && n_free_bufs)
- {
- vlib_buffer_t *b0;
- /* grab free buffer */
- u32 last_empty_buffer =
- vec_len (nm->rx_buffers[thread_index]) - 1;
- 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;
- n_free_bufs--;
-
- /* copy data */
- u32 bytes_to_copy =
- data_len > n_buffer_bytes ? n_buffer_bytes : data_len;
- b0->current_data = 0;
- clib_memcpy_fast (vlib_buffer_get_current (b0),
- (u8 *) NETMAP_BUF (ring, slot->buf_idx) +
- offset, bytes_to_copy);
-
- /* fill buffer header */
- b0->current_length = bytes_to_copy;
-
- if (offset == 0)
- {
- b0->total_length_not_including_first_buffer = 0;
- b0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
- vnet_buffer (b0)->sw_if_index[VLIB_RX] =
- nif->sw_if_index;
- vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
- first_bi0 = bi0;
- first_b0 = vlib_get_buffer (vm, first_bi0);
- }
- else
- buffer_add_to_chain (vm, bi0, first_bi0, prev_bi0);
-
- offset += bytes_to_copy;
- data_len -= bytes_to_copy;
- }
-
- /* trace */
- if (PREDICT_FALSE (n_trace > 0))
- {
- if (PREDICT_TRUE (first_b0 != 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;
- tr->hw_if_index = nif->hw_if_index;
- memcpy (&tr->slot, slot, sizeof (struct netmap_slot));
- }
- }
-
- /* 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,
- next0);
-
- /* next packet */
- n_rx_packets++;
- n_rx_bytes += slot->len;
- to_next[0] = first_bi0;
- to_next += 1;
- n_left_to_next--;
- cur_slot_index = next_slot_index;
-
- r--;
- }
- vlib_put_next_frame (vm, node, next_index, n_left_to_next);
- }
- ring->head = ring->cur = cur_slot_index;
- cur_ring++;
- }
-
- if (n_rx_packets)
- ioctl (nif->fd, NIOCRXSYNC, NULL);
-
- vlib_increment_combined_counter
- (vnet_get_main ()->interface_main.combined_sw_if_counters
- + VNET_INTERFACE_COUNTER_RX,
- vlib_get_thread_index (), nif->hw_if_index, n_rx_packets, n_rx_bytes);
-
- vnet_device_increment_rx_packets (thread_index, n_rx_packets);
-
- return n_rx_packets;
-}
-
-VLIB_NODE_FN (netmap_input_node) (vlib_main_t * vm,
- vlib_node_runtime_t * node,
- vlib_frame_t * frame)
-{
- int i;
- u32 n_rx_packets = 0;
- u32 thread_index = vm->thread_index;
- netmap_main_t *nm = &netmap_main;
- netmap_if_t *nmi;
-
- for (i = 0; i < vec_len (nm->interfaces); i++)
- {
- nmi = vec_elt_at_index (nm->interfaces, i);
- if (nmi->is_admin_up &&
- (i % nm->input_cpu_count) ==
- (thread_index - nm->input_cpu_first_index))
- n_rx_packets += netmap_device_input_fn (vm, node, frame, nmi);
- }
-
- return n_rx_packets;
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (netmap_input_node) = {
- .name = "netmap-input",
- .sibling_of = "device-input",
- .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
- .format_trace = format_netmap_input_trace,
- .type = VLIB_NODE_TYPE_INPUT,
- /* default state is INTERRUPT mode, switch to POLLING if worker threads are enabled */
- .state = VLIB_NODE_STATE_INTERRUPT,
- .n_errors = NETMAP_INPUT_N_ERROR,
- .error_strings = netmap_input_error_strings,
-};
-/* *INDENT-ON* */
-
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/extras/deprecated/perfmon/intel_json_to_c.py b/extras/deprecated/perfmon/intel_json_to_c.py
index 6a625ac2c33..4389c86fc38 100755
--- a/extras/deprecated/perfmon/intel_json_to_c.py
+++ b/extras/deprecated/perfmon/intel_json_to_c.py
@@ -4,48 +4,58 @@ import json, argparse
p = argparse.ArgumentParser()
-p.add_argument('-i', '--input', action="store",
- help="input JSON file name", required = True)
-
-p.add_argument('-o', '--output', action="store",
- help="output C file name", required = True)
-
-p.add_argument('-m', '--model', action="append",
- help="CPU model in format: model[,stepping0]",
- required = True)
+p.add_argument(
+ "-i", "--input", action="store", help="input JSON file name", required=True
+)
+
+p.add_argument(
+ "-o", "--output", action="store", help="output C file name", required=True
+)
+
+p.add_argument(
+ "-m",
+ "--model",
+ action="append",
+ help="CPU model in format: model[,stepping0]",
+ required=True,
+)
r = p.parse_args()
-with open(r.input, 'r') as fp:
+with open(r.input, "r") as fp:
objects = json.load(fp)
-c = open(r.output, 'w')
+c = open(r.output, "w")
-c.write ("""
+c.write(
+ """
#include <perfmon/perfmon_intel.h>
static perfmon_intel_pmc_cpu_model_t cpu_model_table[] = {
-""")
+"""
+)
for v in r.model:
if "," in v:
- (m, s) = v.split(",")
+ (m, s) = v.split(",")
m = int(m, 0)
s = int(s, 0)
- c.write (" {}0x{:02X}, 0x{:02X}, 1{},\n".format("{", m, s, "}"))
+ c.write(" {}0x{:02X}, 0x{:02X}, 1{},\n".format("{", m, s, "}"))
else:
m = int(v, 0)
- c.write (" {}0x{:02X}, 0x00, 0{},\n".format("{", m, "}"))
-c.write ("""
+ c.write(" {}0x{:02X}, 0x00, 0{},\n".format("{", m, "}"))
+c.write(
+ """
};
static perfmon_intel_pmc_event_t event_table[] = {
-""")
+"""
+)
for obj in objects:
MSRIndex = obj["MSRIndex"]
if MSRIndex != "0":
- continue
+ continue
EventCode = obj["EventCode"]
UMask = obj["UMask"]
@@ -53,20 +63,22 @@ for obj in objects:
if "," in EventCode:
continue
- c.write (" {\n")
- c.write (" .event_code = {}{}{},\n".format("{", EventCode, "}"))
- c.write (" .umask = {},\n".format(UMask))
- c.write (" .event_name = \"{}\",\n".format(EventName))
- c.write (" },\n")
+ c.write(" {\n")
+ c.write(" .event_code = {}{}{},\n".format("{", EventCode, "}"))
+ c.write(" .umask = {},\n".format(UMask))
+ c.write(' .event_name = "{}",\n'.format(EventName))
+ c.write(" },\n")
-c.write (""" {
+c.write(
+ """ {
.event_name = 0,
},
};
PERFMON_REGISTER_INTEL_PMC (cpu_model_table, event_table);
-""")
+"""
+)
c.close()
diff --git a/extras/deprecated/plugins/gbp/CMakeLists.txt b/extras/deprecated/plugins/gbp/CMakeLists.txt
new file mode 100644
index 00000000000..95f664ff08e
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/CMakeLists.txt
@@ -0,0 +1,54 @@
+# 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.
+
+add_vpp_plugin(gbp
+ SOURCES
+ gbp_api.c
+ gbp_bridge_domain.c
+ gbp_classify.c
+ gbp_classify_node.c
+ gbp_contract.c
+ gbp_endpoint.c
+ gbp_endpoint_group.c
+ gbp_ext_itf.c
+ gbp_fwd.c
+ gbp_fwd_dpo.c
+ gbp_fwd_node.c
+ gbp_itf.c
+ gbp_learn.c
+ gbp_learn_node.c
+ gbp_policy.c
+ gbp_policy_dpo.c
+ gbp_policy_node.c
+ gbp_recirc.c
+ gbp_route_domain.c
+ gbp_scanner.c
+ gbp_subnet.c
+ gbp_vxlan.c
+ gbp_vxlan_node.c
+
+ MULTIARCH_SOURCES
+ gbp_classify_node.c
+ gbp_fwd_dpo.c
+ gbp_fwd_node.c
+ gbp_learn_node.c
+ gbp_policy_dpo.c
+ gbp_policy_node.c
+ gbp_vxlan_node.c
+
+ API_FILES
+ gbp.api
+
+ INSTALL_HEADERS
+ gbp.h
+)
diff --git a/extras/deprecated/plugins/gbp/gbp.api b/extras/deprecated/plugins/gbp/gbp.api
new file mode 100644
index 00000000000..525e70536bd
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp.api
@@ -0,0 +1,470 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * 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.
+ */
+
+option version = "2.0.0";
+
+import "vnet/ip/ip_types.api";
+import "vnet/ethernet/ethernet_types.api";
+import "vnet/interface_types.api";
+
+enum gbp_bridge_domain_flags
+{
+ GBP_BD_API_FLAG_NONE = 0,
+ GBP_BD_API_FLAG_DO_NOT_LEARN = 1,
+ GBP_BD_API_FLAG_UU_FWD_DROP = 2,
+ GBP_BD_API_FLAG_MCAST_DROP = 4,
+ GBP_BD_API_FLAG_UCAST_ARP = 8,
+};
+
+typedef gbp_bridge_domain
+{
+ u32 bd_id;
+ u32 rd_id;
+ vl_api_gbp_bridge_domain_flags_t flags;
+ vl_api_interface_index_t bvi_sw_if_index;
+ vl_api_interface_index_t uu_fwd_sw_if_index;
+ vl_api_interface_index_t bm_flood_sw_if_index;
+};
+
+ autoreply define gbp_bridge_domain_add
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ vl_api_gbp_bridge_domain_t bd;
+};
+ autoreply define gbp_bridge_domain_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ u32 bd_id;
+};
+autoreply define gbp_bridge_domain_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+define gbp_bridge_domain_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_bridge_domain_t bd;
+};
+
+typedef u16 gbp_scope;
+
+typedef gbp_route_domain
+{
+ u32 rd_id;
+ u32 ip4_table_id;
+ u32 ip6_table_id;
+ vl_api_interface_index_t ip4_uu_sw_if_index;
+ vl_api_interface_index_t ip6_uu_sw_if_index;
+ vl_api_gbp_scope_t scope;
+};
+
+ autoreply define gbp_route_domain_add
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ vl_api_gbp_route_domain_t rd;
+};
+ autoreply define gbp_route_domain_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ u32 rd_id;
+};
+autoreply define gbp_route_domain_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+define gbp_route_domain_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_route_domain_t rd;
+};
+
+/** \brief Endpoint
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+
+enum gbp_endpoint_flags
+{
+ GBP_API_ENDPOINT_FLAG_NONE = 0,
+ GBP_API_ENDPOINT_FLAG_BOUNCE = 0x1,
+ GBP_API_ENDPOINT_FLAG_REMOTE = 0x2,
+ GBP_API_ENDPOINT_FLAG_LEARNT = 0x4,
+ GBP_API_ENDPOINT_FLAG_EXTERNAL = 0x8,
+};
+
+typedef gbp_endpoint_tun
+{
+ vl_api_address_t src;
+ vl_api_address_t dst;
+};
+
+typedef gbp_endpoint
+{
+ vl_api_interface_index_t sw_if_index;
+ u16 sclass;
+ vl_api_gbp_endpoint_flags_t flags;
+ vl_api_mac_address_t mac;
+ vl_api_gbp_endpoint_tun_t tun;
+ u8 n_ips;
+ vl_api_address_t ips[n_ips];
+};
+
+ define gbp_endpoint_add
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ vl_api_gbp_endpoint_t endpoint;
+};
+
+define gbp_endpoint_add_reply
+{
+ option status="in_progress";
+ u32 context;
+ i32 retval;
+ u32 handle;
+};
+
+ autoreply define gbp_endpoint_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ u32 handle;
+};
+
+define gbp_endpoint_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_endpoint_details
+{
+ option status="in_progress";
+ u32 context;
+ f64 age;
+ u32 handle;
+ vl_api_gbp_endpoint_t endpoint;
+};
+
+typedef gbp_endpoint_retention
+{
+ u32 remote_ep_timeout;
+};
+
+typedef gbp_endpoint_group
+{
+ u32 vnid;
+ u16 sclass;
+ u32 bd_id;
+ u32 rd_id;
+ vl_api_interface_index_t uplink_sw_if_index;
+ vl_api_gbp_endpoint_retention_t retention;
+};
+
+ autoreply define gbp_endpoint_group_add
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ vl_api_gbp_endpoint_group_t epg;
+};
+ autoreply define gbp_endpoint_group_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ u16 sclass;
+};
+
+define gbp_endpoint_group_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_endpoint_group_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_endpoint_group_t epg;
+};
+
+typedef gbp_recirc
+{
+ vl_api_interface_index_t sw_if_index;
+ u16 sclass;
+ bool is_ext;
+};
+
+ autoreply define gbp_recirc_add_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ bool is_add;
+ vl_api_gbp_recirc_t recirc;
+};
+
+define gbp_recirc_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_recirc_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_recirc_t recirc;
+};
+
+enum gbp_subnet_type
+{
+ GBP_API_SUBNET_TRANSPORT,
+ GBP_API_SUBNET_STITCHED_INTERNAL,
+ GBP_API_SUBNET_STITCHED_EXTERNAL,
+ GBP_API_SUBNET_L3_OUT,
+ GBP_API_SUBNET_ANON_L3_OUT,
+};
+
+typedef gbp_subnet
+{
+ u32 rd_id;
+ vl_api_interface_index_t sw_if_index [default= 0xffffffff];
+ u16 sclass [default=0xffffffff];
+ vl_api_gbp_subnet_type_t type;
+ vl_api_prefix_t prefix;
+};
+
+ autoreply define gbp_subnet_add_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ bool is_add;
+ vl_api_gbp_subnet_t subnet;
+};
+
+define gbp_subnet_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_subnet_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_subnet_t subnet;
+};
+
+typedef gbp_next_hop
+{
+ vl_api_address_t ip;
+ vl_api_mac_address_t mac;
+ u32 bd_id;
+ u32 rd_id;
+};
+
+enum gbp_hash_mode
+{
+ GBP_API_HASH_MODE_SRC_IP,
+ GBP_API_HASH_MODE_DST_IP,
+ GBP_API_HASH_MODE_SYMMETRIC,
+};
+
+typedef gbp_next_hop_set
+{
+ vl_api_gbp_hash_mode_t hash_mode;
+ u8 n_nhs;
+ vl_api_gbp_next_hop_t nhs[8];
+};
+
+enum gbp_rule_action
+{
+ GBP_API_RULE_PERMIT,
+ GBP_API_RULE_DENY,
+ GBP_API_RULE_REDIRECT,
+};
+
+typedef gbp_rule
+{
+ vl_api_gbp_rule_action_t action;
+ vl_api_gbp_next_hop_set_t nh_set;
+};
+
+typedef gbp_contract
+{
+ vl_api_gbp_scope_t scope;
+ u16 sclass;
+ u16 dclass;
+ u32 acl_index;
+ u8 n_ether_types;
+ u16 allowed_ethertypes[16];
+ u8 n_rules;
+ vl_api_gbp_rule_t rules[n_rules];
+};
+
+ define gbp_contract_add_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ bool is_add;
+ vl_api_gbp_contract_t contract;
+};
+define gbp_contract_add_del_reply
+{
+ option status="in_progress";
+ u32 context;
+ i32 retval;
+ u32 stats_index;
+};
+
+define gbp_contract_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_contract_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_contract_t contract;
+};
+
+/**
+ * @brief Configure a 'base' tunnel from which learned tunnels
+ * are permitted to derive
+ * A base tunnel consists only of the VNI, any src,dst IP
+ * pair is thus allowed.
+ */
+enum gbp_vxlan_tunnel_mode
+{
+ GBP_VXLAN_TUNNEL_MODE_L2,
+ GBP_VXLAN_TUNNEL_MODE_L3,
+};
+
+typedef gbp_vxlan_tunnel
+{
+ u32 vni;
+ vl_api_gbp_vxlan_tunnel_mode_t mode;
+ u32 bd_rd_id;
+ vl_api_ip4_address_t src;
+};
+
+ define gbp_vxlan_tunnel_add
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ vl_api_gbp_vxlan_tunnel_t tunnel;
+};
+
+define gbp_vxlan_tunnel_add_reply
+{
+ option status="in_progress";
+ u32 context;
+ i32 retval;
+ vl_api_interface_index_t sw_if_index;
+};
+
+ autoreply define gbp_vxlan_tunnel_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ u32 vni;
+};
+
+define gbp_vxlan_tunnel_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_vxlan_tunnel_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_vxlan_tunnel_t tunnel;
+};
+
+enum gbp_ext_itf_flags
+{
+ GBP_API_EXT_ITF_F_NONE = 0,
+ GBP_API_EXT_ITF_F_ANON = 1,
+};
+
+typedef gbp_ext_itf
+{
+ vl_api_interface_index_t sw_if_index;
+ u32 bd_id;
+ u32 rd_id;
+ vl_api_gbp_ext_itf_flags_t flags;
+};
+
+ autoreply define gbp_ext_itf_add_del
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+ bool is_add;
+ vl_api_gbp_ext_itf_t ext_itf;
+};
+
+define gbp_ext_itf_dump
+{
+ option status="in_progress";
+ u32 client_index;
+ u32 context;
+};
+
+define gbp_ext_itf_details
+{
+ option status="in_progress";
+ u32 context;
+ vl_api_gbp_ext_itf_t ext_itf;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp.h b/extras/deprecated/plugins/gbp/gbp.h
new file mode 100644
index 00000000000..50039b3bdcf
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+/**
+ * Group Base Policy (GBP) defines:
+ * - endpoints: typically a VM or container that is connected to the
+ * virtual switch/router (i.e. to VPP)
+ * - endpoint-group: (EPG) a collection of endpoints
+ * - policy: rules determining which traffic can pass between EPGs a.k.a
+ * a 'contract'
+ *
+ * Here, policy is implemented via an ACL.
+ * EPG classification for transit packets is determined by:
+ * - source EPG: from the packet's input interface
+ * - destination EPG: from the packet's destination IP address.
+ *
+ */
+
+#ifndef __GBP_H__
+#define __GBP_H__
+
+#include <plugins/acl/exports.h>
+
+#include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_subnet.h>
+#include <plugins/gbp/gbp_recirc.h>
+
+typedef struct
+{
+ u32 gbp_acl_user_id;
+ acl_plugin_methods_t acl_plugin;
+} gbp_main_t;
+
+extern gbp_main_t gbp_main;
+
+typedef enum gbp_policy_type_t_
+{
+ GBP_POLICY_PORT,
+ GBP_POLICY_MAC,
+ GBP_POLICY_LPM,
+ GBP_N_POLICY
+#define GBP_N_POLICY GBP_N_POLICY
+} gbp_policy_type_t;
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_policy_main_t_
+{
+ /**
+ * Next nodes for L2 output features
+ */
+ u32 l2_output_feat_next[GBP_N_POLICY][32];
+} gbp_policy_main_t;
+
+extern gbp_policy_main_t gbp_policy_main;
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_api.c b/extras/deprecated/plugins/gbp/gbp_api.c
new file mode 100644
index 00000000000..ab89172b1af
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_api.c
@@ -0,0 +1,1154 @@
+/*
+ *------------------------------------------------------------------
+ * 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 <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+
+#include <vnet/interface.h>
+#include <vnet/api_errno.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/ethernet/ethernet_types_api.h>
+#include <vpp/app/version.h>
+
+#include <gbp/gbp.h>
+#include <gbp/gbp_learn.h>
+#include <gbp/gbp_itf.h>
+#include <gbp/gbp_vxlan.h>
+#include <gbp/gbp_bridge_domain.h>
+#include <gbp/gbp_route_domain.h>
+#include <gbp/gbp_ext_itf.h>
+#include <gbp/gbp_contract.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+/* define message IDs */
+#include <gbp/gbp.api_enum.h>
+#include <gbp/gbp.api_types.h>
+#include <vnet/format_fns.h>
+#include <vlibapi/api_helper_macros.h>
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+
+gbp_main_t gbp_main;
+
+static u16 msg_id_base;
+
+#define GBP_MSG_BASE msg_id_base
+
+static gbp_endpoint_flags_t
+gbp_endpoint_flags_decode (vl_api_gbp_endpoint_flags_t v)
+{
+ gbp_endpoint_flags_t f = GBP_ENDPOINT_FLAG_NONE;
+
+ v = ntohl (v);
+
+ if (v & GBP_API_ENDPOINT_FLAG_BOUNCE)
+ f |= GBP_ENDPOINT_FLAG_BOUNCE;
+ if (v & GBP_API_ENDPOINT_FLAG_REMOTE)
+ f |= GBP_ENDPOINT_FLAG_REMOTE;
+ if (v & GBP_API_ENDPOINT_FLAG_LEARNT)
+ f |= GBP_ENDPOINT_FLAG_LEARNT;
+ if (v & GBP_API_ENDPOINT_FLAG_EXTERNAL)
+ f |= GBP_ENDPOINT_FLAG_EXTERNAL;
+
+ return (f);
+}
+
+static vl_api_gbp_endpoint_flags_t
+gbp_endpoint_flags_encode (gbp_endpoint_flags_t f)
+{
+ vl_api_gbp_endpoint_flags_t v = 0;
+
+
+ if (f & GBP_ENDPOINT_FLAG_BOUNCE)
+ v |= GBP_API_ENDPOINT_FLAG_BOUNCE;
+ if (f & GBP_ENDPOINT_FLAG_REMOTE)
+ v |= GBP_API_ENDPOINT_FLAG_REMOTE;
+ if (f & GBP_ENDPOINT_FLAG_LEARNT)
+ v |= GBP_API_ENDPOINT_FLAG_LEARNT;
+ if (f & GBP_ENDPOINT_FLAG_EXTERNAL)
+ v |= GBP_API_ENDPOINT_FLAG_EXTERNAL;
+
+ v = htonl (v);
+
+ return (v);
+}
+
+static void
+vl_api_gbp_endpoint_add_t_handler (vl_api_gbp_endpoint_add_t * mp)
+{
+ vl_api_gbp_endpoint_add_reply_t *rmp;
+ gbp_endpoint_flags_t gef;
+ u32 sw_if_index, handle;
+ ip46_address_t *ips;
+ mac_address_t mac;
+ int rv = 0, ii;
+
+ handle = INDEX_INVALID;
+
+ VALIDATE_SW_IF_INDEX (&(mp->endpoint));
+
+ gef = gbp_endpoint_flags_decode (mp->endpoint.flags), ips = NULL;
+ sw_if_index = ntohl (mp->endpoint.sw_if_index);
+
+ if (mp->endpoint.n_ips)
+ {
+ vec_validate (ips, mp->endpoint.n_ips - 1);
+
+ vec_foreach_index (ii, ips)
+ {
+ ip_address_decode (&mp->endpoint.ips[ii], &ips[ii]);
+ }
+ }
+ mac_address_decode (mp->endpoint.mac, &mac);
+
+ if (GBP_ENDPOINT_FLAG_REMOTE & gef)
+ {
+ ip46_address_t tun_src, tun_dst;
+
+ ip_address_decode (&mp->endpoint.tun.src, &tun_src);
+ ip_address_decode (&mp->endpoint.tun.dst, &tun_dst);
+
+ rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP,
+ sw_if_index, ips, &mac,
+ INDEX_INVALID, INDEX_INVALID,
+ ntohs (mp->endpoint.sclass),
+ gef, &tun_src, &tun_dst, &handle);
+ }
+ else
+ {
+ rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP,
+ sw_if_index, ips, &mac,
+ INDEX_INVALID, INDEX_INVALID,
+ ntohs (mp->endpoint.sclass),
+ gef, NULL, NULL, &handle);
+ }
+ vec_free (ips);
+ BAD_SW_IF_INDEX_LABEL;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_GBP_ENDPOINT_ADD_REPLY + GBP_MSG_BASE,
+ ({
+ rmp->handle = htonl (handle);
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_gbp_endpoint_del_t_handler (vl_api_gbp_endpoint_del_t * mp)
+{
+ vl_api_gbp_endpoint_del_reply_t *rmp;
+ int rv = 0;
+
+ gbp_endpoint_unlock (GBP_ENDPOINT_SRC_CP, ntohl (mp->handle));
+
+ REPLY_MACRO (VL_API_GBP_ENDPOINT_DEL_REPLY + GBP_MSG_BASE);
+}
+
+typedef struct gbp_walk_ctx_t_
+{
+ vl_api_registration_t *reg;
+ u32 context;
+} gbp_walk_ctx_t;
+
+static walk_rc_t
+gbp_endpoint_send_details (index_t gei, void *args)
+{
+ vl_api_gbp_endpoint_details_t *mp;
+ gbp_endpoint_loc_t *gel;
+ gbp_endpoint_fwd_t *gef;
+ gbp_endpoint_t *ge;
+ gbp_walk_ctx_t *ctx;
+ u8 n_ips, ii;
+
+ ctx = args;
+ ge = gbp_endpoint_get (gei);
+
+ n_ips = vec_len (ge->ge_key.gek_ips);
+ mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (*mp->endpoint.ips) * n_ips));
+ if (!mp)
+ return 1;
+
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ gel = &ge->ge_locs[0];
+ gef = &ge->ge_fwd;
+
+ if (gbp_endpoint_is_remote (ge))
+ {
+ mp->endpoint.sw_if_index = ntohl (gel->tun.gel_parent_sw_if_index);
+ ip_address_encode (&gel->tun.gel_src, IP46_TYPE_ANY,
+ &mp->endpoint.tun.src);
+ ip_address_encode (&gel->tun.gel_dst, IP46_TYPE_ANY,
+ &mp->endpoint.tun.dst);
+ }
+ else
+ {
+ mp->endpoint.sw_if_index =
+ ntohl (gbp_itf_get_sw_if_index (gef->gef_itf));
+ }
+ mp->endpoint.sclass = ntohs (ge->ge_fwd.gef_sclass);
+ mp->endpoint.n_ips = n_ips;
+ mp->endpoint.flags = gbp_endpoint_flags_encode (gef->gef_flags);
+ mp->handle = htonl (gei);
+ mp->age =
+ clib_host_to_net_f64 (vlib_time_now (vlib_get_main ()) -
+ ge->ge_last_time);
+ mac_address_encode (&ge->ge_key.gek_mac, mp->endpoint.mac);
+
+ vec_foreach_index (ii, ge->ge_key.gek_ips)
+ {
+ ip_address_encode (&ge->ge_key.gek_ips[ii].fp_addr,
+ IP46_TYPE_ANY, &mp->endpoint.ips[ii]);
+ }
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_endpoint_walk (gbp_endpoint_send_details, &ctx);
+}
+
+static void
+gbp_retention_decode (const vl_api_gbp_endpoint_retention_t * in,
+ gbp_endpoint_retention_t * out)
+{
+ out->remote_ep_timeout = ntohl (in->remote_ep_timeout);
+}
+
+static void
+ vl_api_gbp_endpoint_group_add_t_handler
+ (vl_api_gbp_endpoint_group_add_t * mp)
+{
+ vl_api_gbp_endpoint_group_add_reply_t *rmp;
+ gbp_endpoint_retention_t retention;
+ int rv = 0;
+
+ gbp_retention_decode (&mp->epg.retention, &retention);
+
+ rv = gbp_endpoint_group_add_and_lock (ntohl (mp->epg.vnid),
+ ntohs (mp->epg.sclass),
+ ntohl (mp->epg.bd_id),
+ ntohl (mp->epg.rd_id),
+ ntohl (mp->epg.uplink_sw_if_index),
+ &retention);
+
+ REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_REPLY + GBP_MSG_BASE);
+}
+
+static void
+ vl_api_gbp_endpoint_group_del_t_handler
+ (vl_api_gbp_endpoint_group_del_t * mp)
+{
+ vl_api_gbp_endpoint_group_del_reply_t *rmp;
+ int rv = 0;
+
+ rv = gbp_endpoint_group_delete (ntohs (mp->sclass));
+
+ REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static gbp_bridge_domain_flags_t
+gbp_bridge_domain_flags_from_api (vl_api_gbp_bridge_domain_flags_t a)
+{
+ gbp_bridge_domain_flags_t g;
+
+ g = GBP_BD_FLAG_NONE;
+ a = clib_net_to_host_u32 (a);
+
+ if (a & GBP_BD_API_FLAG_DO_NOT_LEARN)
+ g |= GBP_BD_FLAG_DO_NOT_LEARN;
+ if (a & GBP_BD_API_FLAG_UU_FWD_DROP)
+ g |= GBP_BD_FLAG_UU_FWD_DROP;
+ if (a & GBP_BD_API_FLAG_MCAST_DROP)
+ g |= GBP_BD_FLAG_MCAST_DROP;
+ if (a & GBP_BD_API_FLAG_UCAST_ARP)
+ g |= GBP_BD_FLAG_UCAST_ARP;
+
+ return (g);
+}
+
+static void
+vl_api_gbp_bridge_domain_add_t_handler (vl_api_gbp_bridge_domain_add_t * mp)
+{
+ vl_api_gbp_bridge_domain_add_reply_t *rmp;
+ int rv = 0;
+
+ rv = gbp_bridge_domain_add_and_lock (ntohl (mp->bd.bd_id),
+ ntohl (mp->bd.rd_id),
+ gbp_bridge_domain_flags_from_api
+ (mp->bd.flags),
+ ntohl (mp->bd.bvi_sw_if_index),
+ ntohl (mp->bd.uu_fwd_sw_if_index),
+ ntohl (mp->bd.bm_flood_sw_if_index));
+
+ REPLY_MACRO (VL_API_GBP_BRIDGE_DOMAIN_ADD_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_bridge_domain_del_t_handler (vl_api_gbp_bridge_domain_del_t * mp)
+{
+ vl_api_gbp_bridge_domain_del_reply_t *rmp;
+ int rv = 0;
+
+ rv = gbp_bridge_domain_delete (ntohl (mp->bd_id));
+
+ REPLY_MACRO (VL_API_GBP_BRIDGE_DOMAIN_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_route_domain_add_t_handler (vl_api_gbp_route_domain_add_t * mp)
+{
+ vl_api_gbp_route_domain_add_reply_t *rmp;
+ int rv = 0;
+
+ rv = gbp_route_domain_add_and_lock (ntohl (mp->rd.rd_id),
+ ntohs (mp->rd.scope),
+ ntohl (mp->rd.ip4_table_id),
+ ntohl (mp->rd.ip6_table_id),
+ ntohl (mp->rd.ip4_uu_sw_if_index),
+ ntohl (mp->rd.ip6_uu_sw_if_index));
+
+ REPLY_MACRO (VL_API_GBP_ROUTE_DOMAIN_ADD_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_route_domain_del_t_handler (vl_api_gbp_route_domain_del_t * mp)
+{
+ vl_api_gbp_route_domain_del_reply_t *rmp;
+ int rv = 0;
+
+ rv = gbp_route_domain_delete (ntohl (mp->rd_id));
+
+ REPLY_MACRO (VL_API_GBP_ROUTE_DOMAIN_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static int
+gub_subnet_type_from_api (vl_api_gbp_subnet_type_t a, gbp_subnet_type_t * t)
+{
+ a = clib_net_to_host_u32 (a);
+
+ switch (a)
+ {
+ case GBP_API_SUBNET_TRANSPORT:
+ *t = GBP_SUBNET_TRANSPORT;
+ return (0);
+ case GBP_API_SUBNET_L3_OUT:
+ *t = GBP_SUBNET_L3_OUT;
+ return (0);
+ case GBP_API_SUBNET_ANON_L3_OUT:
+ *t = GBP_SUBNET_ANON_L3_OUT;
+ return (0);
+ case GBP_API_SUBNET_STITCHED_INTERNAL:
+ *t = GBP_SUBNET_STITCHED_INTERNAL;
+ return (0);
+ case GBP_API_SUBNET_STITCHED_EXTERNAL:
+ *t = GBP_SUBNET_STITCHED_EXTERNAL;
+ return (0);
+ }
+
+ return (-1);
+}
+
+static void
+vl_api_gbp_subnet_add_del_t_handler (vl_api_gbp_subnet_add_del_t * mp)
+{
+ vl_api_gbp_subnet_add_del_reply_t *rmp;
+ gbp_subnet_type_t type;
+ fib_prefix_t pfx;
+ int rv = 0;
+
+ ip_prefix_decode (&mp->subnet.prefix, &pfx);
+
+ rv = gub_subnet_type_from_api (mp->subnet.type, &type);
+
+ if (0 != rv)
+ goto out;
+
+ if (mp->is_add)
+ rv = gbp_subnet_add (ntohl (mp->subnet.rd_id),
+ &pfx, type,
+ ntohl (mp->subnet.sw_if_index),
+ ntohs (mp->subnet.sclass));
+ else
+ rv = gbp_subnet_del (ntohl (mp->subnet.rd_id), &pfx);
+
+out:
+ REPLY_MACRO (VL_API_GBP_SUBNET_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static vl_api_gbp_subnet_type_t
+gub_subnet_type_to_api (gbp_subnet_type_t t)
+{
+ vl_api_gbp_subnet_type_t a = 0;
+
+ switch (t)
+ {
+ case GBP_SUBNET_TRANSPORT:
+ a = GBP_API_SUBNET_TRANSPORT;
+ break;
+ case GBP_SUBNET_STITCHED_INTERNAL:
+ a = GBP_API_SUBNET_STITCHED_INTERNAL;
+ break;
+ case GBP_SUBNET_STITCHED_EXTERNAL:
+ a = GBP_API_SUBNET_STITCHED_EXTERNAL;
+ break;
+ case GBP_SUBNET_L3_OUT:
+ a = GBP_API_SUBNET_L3_OUT;
+ break;
+ case GBP_SUBNET_ANON_L3_OUT:
+ a = GBP_API_SUBNET_ANON_L3_OUT;
+ break;
+ }
+
+ a = clib_host_to_net_u32 (a);
+
+ return (a);
+}
+
+static walk_rc_t
+gbp_subnet_send_details (u32 rd_id,
+ const fib_prefix_t * pfx,
+ gbp_subnet_type_t type,
+ u32 sw_if_index, sclass_t sclass, void *args)
+{
+ vl_api_gbp_subnet_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return 1;
+
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_SUBNET_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->subnet.type = gub_subnet_type_to_api (type);
+ mp->subnet.sw_if_index = ntohl (sw_if_index);
+ mp->subnet.sclass = ntohs (sclass);
+ mp->subnet.rd_id = ntohl (rd_id);
+ ip_prefix_encode (pfx, &mp->subnet.prefix);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_gbp_subnet_dump_t_handler (vl_api_gbp_subnet_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_subnet_walk (gbp_subnet_send_details, &ctx);
+}
+
+static int
+gbp_endpoint_group_send_details (gbp_endpoint_group_t * gg, void *args)
+{
+ vl_api_gbp_endpoint_group_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return 1;
+
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_GROUP_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->epg.uplink_sw_if_index = ntohl (gg->gg_uplink_sw_if_index);
+ mp->epg.vnid = ntohl (gg->gg_vnid);
+ mp->epg.sclass = ntohs (gg->gg_sclass);
+ mp->epg.bd_id = ntohl (gbp_endpoint_group_get_bd_id (gg));
+ mp->epg.rd_id = ntohl (gbp_route_domain_get_rd_id (gg->gg_rd));
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (1);
+}
+
+static void
+vl_api_gbp_endpoint_group_dump_t_handler (vl_api_gbp_endpoint_group_dump_t *
+ mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_endpoint_group_walk (gbp_endpoint_group_send_details, &ctx);
+}
+
+static int
+gbp_bridge_domain_send_details (gbp_bridge_domain_t * gb, void *args)
+{
+ vl_api_gbp_bridge_domain_details_t *mp;
+ gbp_route_domain_t *gr;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return 1;
+
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_BRIDGE_DOMAIN_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ gr = gbp_route_domain_get (gb->gb_rdi);
+
+ mp->bd.bd_id = ntohl (gb->gb_bd_id);
+ mp->bd.rd_id = ntohl (gr->grd_id);
+ mp->bd.bvi_sw_if_index = ntohl (gb->gb_bvi_sw_if_index);
+ mp->bd.uu_fwd_sw_if_index = ntohl (gb->gb_uu_fwd_sw_if_index);
+ mp->bd.bm_flood_sw_if_index =
+ ntohl (gbp_itf_get_sw_if_index (gb->gb_bm_flood_itf));
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (1);
+}
+
+static void
+vl_api_gbp_bridge_domain_dump_t_handler (vl_api_gbp_bridge_domain_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_bridge_domain_walk (gbp_bridge_domain_send_details, &ctx);
+}
+
+static int
+gbp_route_domain_send_details (gbp_route_domain_t * grd, void *args)
+{
+ vl_api_gbp_route_domain_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return 1;
+
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_ROUTE_DOMAIN_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->rd.rd_id = ntohl (grd->grd_id);
+ mp->rd.ip4_uu_sw_if_index =
+ ntohl (grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4]);
+ mp->rd.ip6_uu_sw_if_index =
+ ntohl (grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6]);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (1);
+}
+
+static void
+vl_api_gbp_route_domain_dump_t_handler (vl_api_gbp_route_domain_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_route_domain_walk (gbp_route_domain_send_details, &ctx);
+}
+
+static void
+vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp)
+{
+ vl_api_gbp_recirc_add_del_reply_t *rmp;
+ u32 sw_if_index;
+ int rv = 0;
+
+ sw_if_index = ntohl (mp->recirc.sw_if_index);
+ if (!vnet_sw_if_index_is_api_valid (sw_if_index))
+ goto bad_sw_if_index;
+
+ if (mp->is_add)
+ rv = gbp_recirc_add (sw_if_index,
+ ntohs (mp->recirc.sclass), mp->recirc.is_ext);
+ else
+ rv = gbp_recirc_delete (sw_if_index);
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static walk_rc_t
+gbp_recirc_send_details (gbp_recirc_t * gr, void *args)
+{
+ vl_api_gbp_recirc_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return (WALK_STOP);
+
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->recirc.sclass = ntohs (gr->gr_sclass);
+ mp->recirc.sw_if_index = ntohl (gr->gr_sw_if_index);
+ mp->recirc.is_ext = gr->gr_is_ext;
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_gbp_recirc_dump_t_handler (vl_api_gbp_recirc_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_recirc_walk (gbp_recirc_send_details, &ctx);
+}
+
+static void
+vl_api_gbp_ext_itf_add_del_t_handler (vl_api_gbp_ext_itf_add_del_t * mp)
+{
+ vl_api_gbp_ext_itf_add_del_reply_t *rmp;
+ u32 sw_if_index = ~0;
+ vl_api_gbp_ext_itf_t *ext_itf;
+ int rv = 0;
+
+ ext_itf = &mp->ext_itf;
+ if (ext_itf)
+ sw_if_index = ntohl (ext_itf->sw_if_index);
+
+ if (!vnet_sw_if_index_is_api_valid (sw_if_index))
+ goto bad_sw_if_index;
+
+ if (mp->is_add)
+ rv = gbp_ext_itf_add (sw_if_index,
+ ntohl (ext_itf->bd_id), ntohl (ext_itf->rd_id),
+ ntohl (ext_itf->flags));
+ else
+ rv = gbp_ext_itf_delete (sw_if_index);
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_GBP_EXT_ITF_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static walk_rc_t
+gbp_ext_itf_send_details (gbp_ext_itf_t * gx, void *args)
+{
+ vl_api_gbp_ext_itf_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return (WALK_STOP);
+
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_EXT_ITF_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->ext_itf.flags = ntohl (gx->gx_flags);
+ mp->ext_itf.bd_id = ntohl (gbp_bridge_domain_get_bd_id (gx->gx_bd));
+ mp->ext_itf.rd_id = ntohl (gbp_route_domain_get_rd_id (gx->gx_rd));
+ mp->ext_itf.sw_if_index = ntohl (gbp_itf_get_sw_if_index (gx->gx_itf));
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_gbp_ext_itf_dump_t_handler (vl_api_gbp_ext_itf_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_ext_itf_walk (gbp_ext_itf_send_details, &ctx);
+}
+
+static int
+gbp_contract_rule_action_deocde (vl_api_gbp_rule_action_t in,
+ gbp_rule_action_t * out)
+{
+ in = clib_net_to_host_u32 (in);
+
+ switch (in)
+ {
+ case GBP_API_RULE_PERMIT:
+ *out = GBP_RULE_PERMIT;
+ return (0);
+ case GBP_API_RULE_DENY:
+ *out = GBP_RULE_DENY;
+ return (0);
+ case GBP_API_RULE_REDIRECT:
+ *out = GBP_RULE_REDIRECT;
+ return (0);
+ }
+
+ return (-1);
+}
+
+static int
+gbp_hash_mode_decode (vl_api_gbp_hash_mode_t in, gbp_hash_mode_t * out)
+{
+ in = clib_net_to_host_u32 (in);
+
+ switch (in)
+ {
+ case GBP_API_HASH_MODE_SRC_IP:
+ *out = GBP_HASH_MODE_SRC_IP;
+ return (0);
+ case GBP_API_HASH_MODE_DST_IP:
+ *out = GBP_HASH_MODE_DST_IP;
+ return (0);
+ case GBP_API_HASH_MODE_SYMMETRIC:
+ *out = GBP_HASH_MODE_SYMMETRIC;
+ return (0);
+ }
+
+ return (-2);
+}
+
+static int
+gbp_next_hop_decode (const vl_api_gbp_next_hop_t * in, index_t * gnhi)
+{
+ ip46_address_t ip;
+ mac_address_t mac;
+ index_t grd, gbd;
+
+ gbd = gbp_bridge_domain_find_and_lock (ntohl (in->bd_id));
+
+ if (INDEX_INVALID == gbd)
+ return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
+
+ grd = gbp_route_domain_find_and_lock (ntohl (in->rd_id));
+
+ if (INDEX_INVALID == grd)
+ return (VNET_API_ERROR_NO_SUCH_FIB);
+
+ ip_address_decode (&in->ip, &ip);
+ mac_address_decode (in->mac, &mac);
+
+ *gnhi = gbp_next_hop_alloc (&ip, grd, &mac, gbd);
+
+ return (0);
+}
+
+static int
+gbp_next_hop_set_decode (const vl_api_gbp_next_hop_set_t * in,
+ gbp_hash_mode_t * hash_mode, index_t ** out)
+{
+
+ index_t *gnhis = NULL;
+ int rv;
+ u8 ii;
+
+ rv = gbp_hash_mode_decode (in->hash_mode, hash_mode);
+
+ if (0 != rv)
+ return rv;
+
+ vec_validate (gnhis, in->n_nhs - 1);
+
+ for (ii = 0; ii < in->n_nhs; ii++)
+ {
+ rv = gbp_next_hop_decode (&in->nhs[ii], &gnhis[ii]);
+
+ if (0 != rv)
+ {
+ vec_free (gnhis);
+ break;
+ }
+ }
+
+ *out = gnhis;
+ return (rv);
+}
+
+static int
+gbp_contract_rule_decode (const vl_api_gbp_rule_t * in, index_t * gui)
+{
+ gbp_hash_mode_t hash_mode;
+ gbp_rule_action_t action;
+ index_t *nhs = NULL;
+ int rv;
+
+ rv = gbp_contract_rule_action_deocde (in->action, &action);
+
+ if (0 != rv)
+ return rv;
+
+ if (GBP_RULE_REDIRECT == action)
+ {
+ rv = gbp_next_hop_set_decode (&in->nh_set, &hash_mode, &nhs);
+
+ if (0 != rv)
+ return (rv);
+ }
+ else
+ {
+ hash_mode = GBP_HASH_MODE_SRC_IP;
+ }
+
+ *gui = gbp_rule_alloc (action, hash_mode, nhs);
+
+ return (rv);
+}
+
+static int
+gbp_contract_rules_decode (u8 n_rules,
+ const vl_api_gbp_rule_t * rules, index_t ** out)
+{
+ index_t *guis = NULL;
+ int rv;
+ u8 ii;
+
+ if (0 == n_rules)
+ {
+ *out = NULL;
+ return (0);
+ }
+
+ vec_validate (guis, n_rules - 1);
+
+ for (ii = 0; ii < n_rules; ii++)
+ {
+ rv = gbp_contract_rule_decode (&rules[ii], &guis[ii]);
+
+ if (0 != rv)
+ {
+ index_t *gui;
+ vec_foreach (gui, guis) gbp_rule_free (*gui);
+ vec_free (guis);
+ return (rv);
+ }
+ }
+
+ *out = guis;
+ return (rv);
+}
+
+static void
+vl_api_gbp_contract_add_del_t_handler (vl_api_gbp_contract_add_del_t * mp)
+{
+ vl_api_gbp_contract_add_del_reply_t *rmp;
+ u16 *allowed_ethertypes;
+ u32 stats_index = ~0;
+ index_t *rules;
+ int ii, rv = 0;
+ u8 n_et;
+
+ if (mp->is_add)
+ {
+ rv = gbp_contract_rules_decode (mp->contract.n_rules,
+ mp->contract.rules, &rules);
+ if (0 != rv)
+ goto out;
+
+ allowed_ethertypes = NULL;
+
+ /*
+ * allowed ether types
+ */
+ n_et = mp->contract.n_ether_types;
+ vec_validate (allowed_ethertypes, n_et - 1);
+
+ for (ii = 0; ii < n_et; ii++)
+ {
+ /* leave the ether types in network order */
+ allowed_ethertypes[ii] = mp->contract.allowed_ethertypes[ii];
+ }
+
+ rv = gbp_contract_update (ntohs (mp->contract.scope),
+ ntohs (mp->contract.sclass),
+ ntohs (mp->contract.dclass),
+ ntohl (mp->contract.acl_index),
+ rules, allowed_ethertypes, &stats_index);
+ }
+ else
+ rv = gbp_contract_delete (ntohs (mp->contract.scope),
+ ntohs (mp->contract.sclass),
+ ntohs (mp->contract.dclass));
+
+out:
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_GBP_CONTRACT_ADD_DEL_REPLY + GBP_MSG_BASE,
+ ({
+ rmp->stats_index = htonl (stats_index);
+ }));
+ /* *INDENT-ON* */
+}
+
+static int
+gbp_contract_send_details (gbp_contract_t * gbpc, void *args)
+{
+ vl_api_gbp_contract_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return 1;
+
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_GBP_CONTRACT_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->contract.sclass = ntohs (gbpc->gc_key.gck_src);
+ mp->contract.dclass = ntohs (gbpc->gc_key.gck_dst);
+ mp->contract.acl_index = ntohl (gbpc->gc_acl_index);
+ mp->contract.scope = ntohs (gbpc->gc_key.gck_scope);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (1);
+}
+
+static void
+vl_api_gbp_contract_dump_t_handler (vl_api_gbp_contract_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_contract_walk (gbp_contract_send_details, &ctx);
+}
+
+static int
+gbp_vxlan_tunnel_mode_2_layer (vl_api_gbp_vxlan_tunnel_mode_t mode,
+ gbp_vxlan_tunnel_layer_t * l)
+{
+ mode = clib_net_to_host_u32 (mode);
+
+ switch (mode)
+ {
+ case GBP_VXLAN_TUNNEL_MODE_L2:
+ *l = GBP_VXLAN_TUN_L2;
+ return (0);
+ case GBP_VXLAN_TUNNEL_MODE_L3:
+ *l = GBP_VXLAN_TUN_L3;
+ return (0);
+ }
+ return (-1);
+}
+
+static void
+vl_api_gbp_vxlan_tunnel_add_t_handler (vl_api_gbp_vxlan_tunnel_add_t * mp)
+{
+ vl_api_gbp_vxlan_tunnel_add_reply_t *rmp;
+ gbp_vxlan_tunnel_layer_t layer;
+ ip4_address_t src;
+ u32 sw_if_index;
+ int rv = 0;
+
+ ip4_address_decode (mp->tunnel.src, &src);
+ rv = gbp_vxlan_tunnel_mode_2_layer (mp->tunnel.mode, &layer);
+
+ if (0 != rv)
+ goto out;
+
+ rv = gbp_vxlan_tunnel_add (ntohl (mp->tunnel.vni),
+ layer,
+ ntohl (mp->tunnel.bd_rd_id), &src, &sw_if_index);
+
+out:
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_GBP_VXLAN_TUNNEL_ADD_REPLY + GBP_MSG_BASE,
+ ({
+ rmp->sw_if_index = htonl (sw_if_index);
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_gbp_vxlan_tunnel_del_t_handler (vl_api_gbp_vxlan_tunnel_add_t * mp)
+{
+ vl_api_gbp_vxlan_tunnel_del_reply_t *rmp;
+ int rv = 0;
+
+ rv = gbp_vxlan_tunnel_del (ntohl (mp->tunnel.vni));
+
+ REPLY_MACRO (VL_API_GBP_VXLAN_TUNNEL_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static vl_api_gbp_vxlan_tunnel_mode_t
+gbp_vxlan_tunnel_layer_2_mode (gbp_vxlan_tunnel_layer_t layer)
+{
+ vl_api_gbp_vxlan_tunnel_mode_t mode = GBP_VXLAN_TUNNEL_MODE_L2;
+
+ switch (layer)
+ {
+ case GBP_VXLAN_TUN_L2:
+ mode = GBP_VXLAN_TUNNEL_MODE_L2;
+ break;
+ case GBP_VXLAN_TUN_L3:
+ mode = GBP_VXLAN_TUNNEL_MODE_L3;
+ break;
+ }
+ mode = clib_host_to_net_u32 (mode);
+
+ return (mode);
+}
+
+static walk_rc_t
+gbp_vxlan_tunnel_send_details (gbp_vxlan_tunnel_t * gt, void *args)
+{
+ vl_api_gbp_vxlan_tunnel_details_t *mp;
+ gbp_walk_ctx_t *ctx;
+
+ ctx = args;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return 1;
+
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = htons (VL_API_GBP_VXLAN_TUNNEL_DETAILS + GBP_MSG_BASE);
+ mp->context = ctx->context;
+
+ mp->tunnel.vni = htonl (gt->gt_vni);
+ mp->tunnel.mode = gbp_vxlan_tunnel_layer_2_mode (gt->gt_layer);
+ mp->tunnel.bd_rd_id = htonl (gt->gt_bd_rd_id);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (1);
+}
+
+static void
+vl_api_gbp_vxlan_tunnel_dump_t_handler (vl_api_gbp_vxlan_tunnel_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ gbp_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ gbp_vxlan_walk (gbp_vxlan_tunnel_send_details, &ctx);
+}
+
+#include <gbp/gbp.api.c>
+static clib_error_t *
+gbp_init (vlib_main_t * vm)
+{
+ gbp_main_t *gbpm = &gbp_main;
+
+ gbpm->gbp_acl_user_id = ~0;
+
+ /* Ask for a correctly-sized block of API message decode slots */
+ msg_id_base = setup_message_id_table ();
+
+ return (NULL);
+}
+
+VLIB_API_INIT_FUNCTION (gbp_init);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Group Based Policy (GBP)",
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_bridge_domain.c b/extras/deprecated/plugins/gbp/gbp_bridge_domain.c
new file mode 100644
index 00000000000..279169abb1d
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_bridge_domain.c
@@ -0,0 +1,503 @@
+/*
+ * 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 <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_itf.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/feat_bitmap.h>
+#include <vnet/l2/l2_bvi.h>
+#include <vnet/l2/l2_fib.h>
+
+/**
+ * Pool of GBP bridge_domains
+ */
+gbp_bridge_domain_t *gbp_bridge_domain_pool;
+
+/**
+ * DB of bridge_domains
+ */
+gbp_bridge_domain_db_t gbp_bridge_domain_db;
+
+/**
+ * Map of BD index to contract scope
+ */
+gbp_scope_t *gbp_scope_by_bd_index;
+
+/**
+ * logger
+ */
+vlib_log_class_t gb_logger;
+
+#define GBP_BD_DBG(...) \
+ vlib_log_debug (gb_logger, __VA_ARGS__);
+
+index_t
+gbp_bridge_domain_index (const gbp_bridge_domain_t * gbd)
+{
+ return (gbd - gbp_bridge_domain_pool);
+}
+
+static void
+gbp_bridge_domain_lock (index_t i)
+{
+ gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (i);
+ gb->gb_locks++;
+}
+
+u32
+gbp_bridge_domain_get_bd_id (index_t gbdi)
+{
+ gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (gbdi);
+
+ return (gb->gb_bd_id);
+}
+
+static index_t
+gbp_bridge_domain_find (u32 bd_id)
+{
+ uword *p;
+
+ p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id);
+
+ if (NULL != p)
+ return p[0];
+
+ return (INDEX_INVALID);
+}
+
+index_t
+gbp_bridge_domain_find_and_lock (u32 bd_id)
+{
+ uword *p;
+
+ p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id);
+
+ if (NULL != p)
+ {
+ gbp_bridge_domain_lock (p[0]);
+ return p[0];
+ }
+ return (INDEX_INVALID);
+}
+
+static void
+gbp_bridge_domain_db_add (gbp_bridge_domain_t * gb)
+{
+ index_t gbi = gb - gbp_bridge_domain_pool;
+
+ hash_set (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id, gbi);
+ vec_validate_init_empty (gbp_bridge_domain_db.gbd_by_bd_index,
+ gb->gb_bd_index, INDEX_INVALID);
+ gbp_bridge_domain_db.gbd_by_bd_index[gb->gb_bd_index] = gbi;
+}
+
+static void
+gbp_bridge_domain_db_remove (gbp_bridge_domain_t * gb)
+{
+ hash_unset (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id);
+ gbp_bridge_domain_db.gbd_by_bd_index[gb->gb_bd_index] = INDEX_INVALID;
+}
+
+u8 *
+format_gbp_bridge_domain_flags (u8 * s, va_list * args)
+{
+ gbp_bridge_domain_flags_t gf = va_arg (*args, gbp_bridge_domain_flags_t);
+
+ if (gf)
+ {
+ if (gf & GBP_BD_FLAG_DO_NOT_LEARN)
+ s = format (s, "do-not-learn ");
+ if (gf & GBP_BD_FLAG_UU_FWD_DROP)
+ s = format (s, "uu-fwd-drop ");
+ if (gf & GBP_BD_FLAG_MCAST_DROP)
+ s = format (s, "mcast-drop ");
+ if (gf & GBP_BD_FLAG_UCAST_ARP)
+ s = format (s, "ucast-arp ");
+ }
+ else
+ {
+ s = format (s, "none");
+ }
+ return (s);
+}
+
+static u8 *
+format_gbp_bridge_domain_ptr (u8 * s, va_list * args)
+{
+ gbp_bridge_domain_t *gb = va_arg (*args, gbp_bridge_domain_t *);
+ vnet_main_t *vnm = vnet_get_main ();
+
+ if (NULL != gb)
+ s =
+ format (s,
+ "[%d] bd:[%d,%d], bvi:%U uu-flood:%U bm-flood:%U flags:%U locks:%d",
+ gb - gbp_bridge_domain_pool, gb->gb_bd_id, gb->gb_bd_index,
+ format_vnet_sw_if_index_name, vnm, gb->gb_bvi_sw_if_index,
+ format_vnet_sw_if_index_name, vnm, gb->gb_uu_fwd_sw_if_index,
+ format_gbp_itf_hdl, gb->gb_bm_flood_itf,
+ format_gbp_bridge_domain_flags, gb->gb_flags, gb->gb_locks);
+ else
+ s = format (s, "NULL");
+
+ return (s);
+}
+
+u8 *
+format_gbp_bridge_domain (u8 * s, va_list * args)
+{
+ index_t gbi = va_arg (*args, index_t);
+
+ s =
+ format (s, "%U", format_gbp_bridge_domain_ptr,
+ gbp_bridge_domain_get (gbi));
+
+ return (s);
+}
+
+int
+gbp_bridge_domain_add_and_lock (u32 bd_id,
+ u32 rd_id,
+ gbp_bridge_domain_flags_t flags,
+ u32 bvi_sw_if_index,
+ u32 uu_fwd_sw_if_index,
+ u32 bm_flood_sw_if_index)
+{
+ gbp_bridge_domain_t *gb;
+ index_t gbi;
+
+ gbi = gbp_bridge_domain_find (bd_id);
+
+ if (INDEX_INVALID == gbi)
+ {
+ gbp_route_domain_t *gr;
+ u32 bd_index;
+
+ bd_index = bd_find_index (&bd_main, bd_id);
+
+ if (~0 == bd_index)
+ return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
+
+ bd_flags_t bd_flags = L2_NONE;
+ if (flags & GBP_BD_FLAG_UU_FWD_DROP)
+ bd_flags |= L2_UU_FLOOD;
+ if (flags & GBP_BD_FLAG_MCAST_DROP)
+ bd_flags |= L2_FLOOD;
+
+ pool_get (gbp_bridge_domain_pool, gb);
+ memset (gb, 0, sizeof (*gb));
+
+ gbi = gb - gbp_bridge_domain_pool;
+ gb->gb_bd_id = bd_id;
+ gb->gb_bd_index = bd_index;
+ gb->gb_uu_fwd_sw_if_index = uu_fwd_sw_if_index;
+ gb->gb_bvi_sw_if_index = bvi_sw_if_index;
+ gbp_itf_hdl_reset (&gb->gb_bm_flood_itf);
+ gb->gb_locks = 1;
+ gb->gb_flags = flags;
+ gb->gb_rdi = gbp_route_domain_find_and_lock (rd_id);
+
+ /*
+ * set the scope from the BD's RD's scope
+ */
+ gr = gbp_route_domain_get (gb->gb_rdi);
+ vec_validate (gbp_scope_by_bd_index, gb->gb_bd_index);
+ gbp_scope_by_bd_index[gb->gb_bd_index] = gr->grd_scope;
+
+ /*
+ * Set the BVI and uu-flood interfaces into the BD
+ */
+ gbp_bridge_domain_itf_add (gbi, gb->gb_bvi_sw_if_index,
+ L2_BD_PORT_TYPE_BVI);
+
+ if ((!(flags & GBP_BD_FLAG_UU_FWD_DROP) ||
+ (flags & GBP_BD_FLAG_UCAST_ARP)) &&
+ ~0 != gb->gb_uu_fwd_sw_if_index)
+ gbp_bridge_domain_itf_add (gbi, gb->gb_uu_fwd_sw_if_index,
+ L2_BD_PORT_TYPE_UU_FWD);
+
+ if (!(flags & GBP_BD_FLAG_MCAST_DROP) && ~0 != bm_flood_sw_if_index)
+ {
+ gb->gb_bm_flood_itf =
+ gbp_itf_l2_add_and_lock (bm_flood_sw_if_index, gbi);
+ gbp_itf_l2_set_input_feature (gb->gb_bm_flood_itf,
+ L2INPUT_FEAT_GBP_LEARN);
+ }
+
+ /*
+ * unset any flag(s) set above
+ */
+ bd_set_flags (vlib_get_main (), bd_index, bd_flags, 0);
+
+ if (flags & GBP_BD_FLAG_UCAST_ARP)
+ {
+ bd_flags = L2_ARP_UFWD;
+ bd_set_flags (vlib_get_main (), bd_index, bd_flags, 1);
+ }
+
+ /*
+ * Add the BVI's MAC to the L2FIB
+ */
+ l2fib_add_entry (vnet_sw_interface_get_hw_address
+ (vnet_get_main (), gb->gb_bvi_sw_if_index),
+ gb->gb_bd_index, gb->gb_bvi_sw_if_index,
+ (L2FIB_ENTRY_RESULT_FLAG_STATIC |
+ L2FIB_ENTRY_RESULT_FLAG_BVI));
+
+ gbp_bridge_domain_db_add (gb);
+ }
+ else
+ {
+ gb = gbp_bridge_domain_get (gbi);
+ gb->gb_locks++;
+ }
+
+ GBP_BD_DBG ("add: %U", format_gbp_bridge_domain_ptr, gb);
+
+ return (0);
+}
+
+void
+gbp_bridge_domain_itf_add (index_t gbdi,
+ u32 sw_if_index, l2_bd_port_type_t type)
+{
+ gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (gbdi);
+
+ set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L2_BRIDGE,
+ sw_if_index, gb->gb_bd_index, type, 0, 0);
+ /*
+ * adding an interface to the bridge enables learning on the
+ * interface. Disable learning on the interface by default for gbp
+ * interfaces
+ */
+ l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_LEARN, 0);
+}
+
+void
+gbp_bridge_domain_itf_del (index_t gbdi,
+ u32 sw_if_index, l2_bd_port_type_t type)
+{
+ gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (gbdi);
+
+ set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L3, sw_if_index,
+ gb->gb_bd_index, type, 0, 0);
+}
+
+void
+gbp_bridge_domain_unlock (index_t gbdi)
+{
+ gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (gbdi);
+
+ gb->gb_locks--;
+
+ if (0 == gb->gb_locks)
+ {
+ GBP_BD_DBG ("destroy: %U", format_gbp_bridge_domain_ptr, gb);
+
+ l2fib_del_entry (vnet_sw_interface_get_hw_address
+ (vnet_get_main (), gb->gb_bvi_sw_if_index),
+ gb->gb_bd_index, gb->gb_bvi_sw_if_index);
+
+ gbp_bridge_domain_itf_del (gbdi, gb->gb_bvi_sw_if_index,
+ L2_BD_PORT_TYPE_BVI);
+ if (~0 != gb->gb_uu_fwd_sw_if_index)
+ gbp_bridge_domain_itf_del (gbdi, gb->gb_uu_fwd_sw_if_index,
+ L2_BD_PORT_TYPE_UU_FWD);
+ gbp_itf_unlock (&gb->gb_bm_flood_itf);
+
+ gbp_bridge_domain_db_remove (gb);
+ gbp_route_domain_unlock (gb->gb_rdi);
+
+ pool_put (gbp_bridge_domain_pool, gb);
+ }
+}
+
+int
+gbp_bridge_domain_delete (u32 bd_id)
+{
+ index_t gbi;
+
+ GBP_BD_DBG ("del: %d", bd_id);
+ gbi = gbp_bridge_domain_find (bd_id);
+
+ if (INDEX_INVALID != gbi)
+ {
+ GBP_BD_DBG ("del: %U", format_gbp_bridge_domain, gbi);
+ gbp_bridge_domain_unlock (gbi);
+
+ return (0);
+ }
+
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+void
+gbp_bridge_domain_walk (gbp_bridge_domain_cb_t cb, void *ctx)
+{
+ gbp_bridge_domain_t *gbpe;
+
+ /* *INDENT-OFF* */
+ pool_foreach (gbpe, gbp_bridge_domain_pool)
+ {
+ if (!cb(gbpe, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_bridge_domain_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ gbp_bridge_domain_flags_t flags;
+ u32 bm_flood_sw_if_index = ~0;
+ u32 uu_fwd_sw_if_index = ~0;
+ u32 bd_id = ~0, rd_id = ~0;
+ u32 bvi_sw_if_index = ~0;
+ u8 add = 1;
+
+ flags = GBP_BD_FLAG_NONE;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "bvi %U", unformat_vnet_sw_interface,
+ vnm, &bvi_sw_if_index))
+ ;
+ else if (unformat (input, "uu-fwd %U", unformat_vnet_sw_interface,
+ vnm, &uu_fwd_sw_if_index))
+ ;
+ else if (unformat (input, "bm-flood %U", unformat_vnet_sw_interface,
+ vnm, &bm_flood_sw_if_index))
+ ;
+ else if (unformat (input, "add"))
+ add = 1;
+ else if (unformat (input, "del"))
+ add = 0;
+ else if (unformat (input, "flags %d", &flags))
+ ;
+ else if (unformat (input, "bd %d", &bd_id))
+ ;
+ else if (unformat (input, "rd %d", &rd_id))
+ ;
+ else
+ break;
+ }
+
+ if (~0 == bd_id)
+ return clib_error_return (0, "BD-ID must be specified");
+ if (~0 == rd_id)
+ return clib_error_return (0, "RD-ID must be specified");
+
+ if (add)
+ {
+ if (~0 == bvi_sw_if_index)
+ return clib_error_return (0, "interface must be specified");
+
+ gbp_bridge_domain_add_and_lock (bd_id, rd_id,
+ flags,
+ bvi_sw_if_index,
+ uu_fwd_sw_if_index,
+ bm_flood_sw_if_index);
+ }
+ else
+ gbp_bridge_domain_delete (bd_id);
+
+ return (NULL);
+}
+
+/*?
+ * Configure a GBP bridge-domain
+ *
+ * @cliexpar
+ * @cliexstart{gbp bridge-domain [del] bd <ID> bvi <interface> [uu-fwd <interface>] [bm-flood <interface>] [flags <flags>]}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_bridge_domain_cli_node, static) = {
+ .path = "gbp bridge-domain",
+ .short_help = "gbp bridge-domain [del] bd <ID> bvi <interface> [uu-fwd <interface>] [bm-flood <interface>] [flags <flags>]",
+ .function = gbp_bridge_domain_cli,
+};
+
+static int
+gbp_bridge_domain_show_one (gbp_bridge_domain_t *gb, void *ctx)
+{
+ vlib_main_t *vm;
+
+ vm = ctx;
+ vlib_cli_output (vm, " %U", format_gbp_bridge_domain_ptr, gb);
+
+ return (1);
+}
+
+static clib_error_t *
+gbp_bridge_domain_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vlib_cli_output (vm, "Bridge-Domains:");
+ gbp_bridge_domain_walk (gbp_bridge_domain_show_one, vm);
+
+ return (NULL);
+}
+
+
+/*?
+ * Show Group Based Policy Bridge_Domains and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp bridge_domain}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_bridge_domain_show_node, static) = {
+ .path = "show gbp bridge-domain",
+ .short_help = "show gbp bridge-domain\n",
+ .function = gbp_bridge_domain_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_bridge_domain_init (vlib_main_t * vm)
+{
+ gb_logger = vlib_log_register_class ("gbp", "bd");
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_bridge_domain_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_bridge_domain.h b/extras/deprecated/plugins/gbp/gbp_bridge_domain.h
new file mode 100644
index 00000000000..0449240083c
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_bridge_domain.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_BRIDGE_DOMAIN_H__
+#define __GBP_BRIDGE_DOMAIN_H__
+
+#include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
+
+#include <vnet/fib/fib_types.h>
+#include <vnet/l2/l2_bd.h>
+
+/**
+ * Bridge Domain Flags
+ */
+typedef enum gbp_bridge_domain_flags_t_
+{
+ GBP_BD_FLAG_NONE = 0,
+ GBP_BD_FLAG_DO_NOT_LEARN = (1 << 0),
+ GBP_BD_FLAG_UU_FWD_DROP = (1 << 1),
+ GBP_BD_FLAG_MCAST_DROP = (1 << 2),
+ GBP_BD_FLAG_UCAST_ARP = (1 << 3),
+} gbp_bridge_domain_flags_t;
+
+/**
+ * A bridge Domain Representation.
+ * This is a standard bridge-domain plus all the attributes it must
+ * have to supprt the GBP model.
+ */
+typedef struct gbp_bridge_domain_t_
+{
+ /**
+ * Bridge-domain ID
+ */
+ u32 gb_bd_id;
+ u32 gb_bd_index;
+
+ /**
+ * Index of the Route-domain this BD is associated with. This is used as the
+ * 'scope' of the packets for contract matching.
+ */
+ u32 gb_rdi;
+
+ /**
+ * Flags conttrolling behaviour
+ */
+ gbp_bridge_domain_flags_t gb_flags;
+
+ /**
+ * The BD's BVI interface (obligatory)
+ */
+ u32 gb_bvi_sw_if_index;
+
+ /**
+ * The BD's MAC spine-proxy interface (optional)
+ */
+ u32 gb_uu_fwd_sw_if_index;
+
+ /**
+ * The BD's interface to sned Broadcast and multicast packets
+ */
+ gbp_itf_hdl_t gb_bm_flood_itf;
+
+ /**
+ * The index of the BD's VNI interface on which packets from
+ * unkown endpoints arrive
+ */
+ u32 gb_vni;
+
+ /**
+ * locks/references to the BD so it does not get deleted (from the API)
+ * whilst it is still being used
+ */
+ u32 gb_locks;
+} gbp_bridge_domain_t;
+
+extern void gbp_bridge_domain_itf_add (index_t gbdi,
+ u32 sw_if_index,
+ l2_bd_port_type_t type);
+extern void gbp_bridge_domain_itf_del (index_t gbdi,
+ u32 sw_if_index,
+ l2_bd_port_type_t type);
+
+extern int gbp_bridge_domain_add_and_lock (u32 bd_id,
+ u32 rd_id,
+ gbp_bridge_domain_flags_t flags,
+ u32 bvi_sw_if_index,
+ u32 uu_fwd_sw_if_index,
+ u32 bm_flood_sw_if_index);
+
+extern void gbp_bridge_domain_unlock (index_t gbi);
+extern index_t gbp_bridge_domain_find_and_lock (u32 bd_id);
+extern int gbp_bridge_domain_delete (u32 bd_id);
+extern index_t gbp_bridge_domain_index (const gbp_bridge_domain_t *);
+extern u32 gbp_bridge_domain_get_bd_id (index_t gbdi);
+
+typedef int (*gbp_bridge_domain_cb_t) (gbp_bridge_domain_t * gb, void *ctx);
+extern void gbp_bridge_domain_walk (gbp_bridge_domain_cb_t bgpe, void *ctx);
+
+extern u8 *format_gbp_bridge_domain (u8 * s, va_list * args);
+extern u8 *format_gbp_bridge_domain_flags (u8 * s, va_list * args);
+
+/**
+ * DB of bridge_domains
+ */
+typedef struct gbp_bridge_domain_db_t
+{
+ uword *gbd_by_bd_id;
+ index_t *gbd_by_bd_index;
+} gbp_bridge_domain_db_t;
+
+extern gbp_bridge_domain_db_t gbp_bridge_domain_db;
+extern gbp_bridge_domain_t *gbp_bridge_domain_pool;
+
+always_inline gbp_bridge_domain_t *
+gbp_bridge_domain_get (index_t i)
+{
+ return (pool_elt_at_index (gbp_bridge_domain_pool, i));
+}
+
+always_inline gbp_bridge_domain_t *
+gbp_bridge_domain_get_by_bd_index (u32 bd_index)
+{
+ return (gbp_bridge_domain_get
+ (gbp_bridge_domain_db.gbd_by_bd_index[bd_index]));
+}
+
+extern gbp_scope_t *gbp_scope_by_bd_index;
+
+always_inline gbp_scope_t
+gbp_bridge_domain_get_scope (u32 bd_index)
+{
+ return (gbp_scope_by_bd_index[bd_index]);
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_classify.c b/extras/deprecated/plugins/gbp/gbp_classify.c
new file mode 100644
index 00000000000..255db252871
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_classify.c
@@ -0,0 +1,71 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_classify.h>
+#include <vnet/l2/l2_input.h>
+
+gbp_src_classify_main_t gbp_src_classify_main;
+
+static clib_error_t *
+gbp_src_classify_init (vlib_main_t * vm)
+{
+ gbp_src_classify_main_t *em = &gbp_src_classify_main;
+
+ vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-src-classify");
+
+ /* Initialize the feature next-node indexes */
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ em->l2_input_feat_next[GBP_SRC_CLASSIFY_NULL]);
+
+ node = vlib_get_node_by_name (vm, (u8 *) "gbp-null-classify");
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ em->l2_input_feat_next[GBP_SRC_CLASSIFY_PORT]);
+
+ node = vlib_get_node_by_name (vm, (u8 *) "l2-gbp-lpm-classify");
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ em->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM]);
+
+ node = vlib_get_node_by_name (vm, (u8 *) "l2-gbp-lpm-anon-classify");
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ em->l2_input_feat_next
+ [GBP_SRC_CLASSIFY_LPM_ANON]);
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_src_classify_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_classify.h b/extras/deprecated/plugins/gbp/gbp_classify.h
new file mode 100644
index 00000000000..ca7db94a2c0
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_classify.h
@@ -0,0 +1,94 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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.
+ */
+
+#ifndef __GBP_CLASSIFY_H__
+#define __GBP_CLASSIFY_H__
+
+#include <plugins/gbp/gbp.h>
+#include <vnet/ethernet/arp_packet.h>
+
+typedef enum gbp_src_classify_type_t_
+{
+ GBP_SRC_CLASSIFY_NULL,
+ GBP_SRC_CLASSIFY_PORT,
+ GBP_SRC_CLASSIFY_LPM,
+ GBP_SRC_CLASSIFY_LPM_ANON,
+ GBP_SRC_N_CLASSIFY
+#define GBP_SRC_N_CLASSIFY GBP_SRC_N_CLASSIFY
+} gbp_src_classify_type_t;
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_src_classify_main_t_
+{
+ /**
+ * Next nodes for L2 output features
+ */
+ u32 l2_input_feat_next[GBP_SRC_N_CLASSIFY][32];
+} gbp_src_classify_main_t;
+
+extern gbp_src_classify_main_t gbp_src_classify_main;
+
+enum gbp_classify_get_ip_way
+{
+ GBP_CLASSIFY_GET_IP_SRC = 0,
+ GBP_CLASSIFY_GET_IP_DST = 1
+};
+
+static_always_inline dpo_proto_t
+gbp_classify_get_ip_address (const ethernet_header_t * eh0,
+ const ip4_address_t ** ip4,
+ const ip6_address_t ** ip6,
+ const enum gbp_classify_get_ip_way way)
+{
+ u16 etype = clib_net_to_host_u16 (eh0->type);
+ const void *l3h0 = eh0 + 1;
+
+ if (ETHERNET_TYPE_VLAN == etype)
+ {
+ const ethernet_vlan_header_t *vh0 =
+ (ethernet_vlan_header_t *) (eh0 + 1);
+ etype = clib_net_to_host_u16 (vh0->type);
+ l3h0 = vh0 + 1;
+ }
+
+ switch (etype)
+ {
+ case ETHERNET_TYPE_IP4:
+ *ip4 = &(&((const ip4_header_t *) l3h0)->src_address)[way];
+ return DPO_PROTO_IP4;
+ case ETHERNET_TYPE_IP6:
+ *ip6 = &(&((const ip6_header_t *) l3h0)->src_address)[way];
+ return DPO_PROTO_IP6;
+ case ETHERNET_TYPE_ARP:
+ *ip4 = &((ethernet_arp_header_t *) l3h0)->ip4_over_ethernet[way].ip4;
+ return DPO_PROTO_IP4;
+ }
+
+ return DPO_PROTO_NONE;
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_classify_node.c b/extras/deprecated/plugins/gbp/gbp_classify_node.c
new file mode 100644
index 00000000000..a2058a21284
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_classify_node.c
@@ -0,0 +1,628 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_classify.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_ext_itf.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/dpo/load_balance.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/feat_bitmap.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+#include <vnet/ethernet/arp_packet.h>
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_classify_trace_t_
+{
+ /* per-pkt trace data */
+ sclass_t sclass;
+} gbp_classify_trace_t;
+
+/*
+ * determine the SRC EPG form the input port
+ */
+always_inline uword
+gbp_classify_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame,
+ gbp_src_classify_type_t type, dpo_proto_t dproto)
+{
+ gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
+ u32 n_left_from, *from, *to_next;
+ u32 next_index;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 next0, bi0, sw_if_index0;
+ const gbp_endpoint_t *ge0;
+ vlib_buffer_t *b0;
+ sclass_t sclass0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;
+
+ if (GBP_SRC_CLASSIFY_NULL == type)
+ {
+ sclass0 = SCLASS_INVALID;
+ next0 =
+ vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
+ L2INPUT_FEAT_GBP_NULL_CLASSIFY);
+ }
+ else
+ {
+ if (DPO_PROTO_ETHERNET == dproto)
+ {
+ const ethernet_header_t *h0;
+
+ h0 = vlib_buffer_get_current (b0);
+ next0 =
+ vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
+ L2INPUT_FEAT_GBP_SRC_CLASSIFY);
+ ge0 = gbp_endpoint_find_mac (h0->src_address,
+ vnet_buffer (b0)->l2.bd_index);
+ }
+ else if (DPO_PROTO_IP4 == dproto)
+ {
+ const ip4_header_t *h0;
+
+ h0 = vlib_buffer_get_current (b0);
+
+ ge0 = gbp_endpoint_find_ip4
+ (&h0->src_address,
+ fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
+ sw_if_index0));
+
+
+ /*
+ * Go straight to looukp, do not pass go, do not collect $200
+ */
+ next0 = 0;
+ }
+ else if (DPO_PROTO_IP6 == dproto)
+ {
+ const ip6_header_t *h0;
+
+ h0 = vlib_buffer_get_current (b0);
+
+ ge0 = gbp_endpoint_find_ip6
+ (&h0->src_address,
+ fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
+ sw_if_index0));
+
+
+ /*
+ * Go straight to lookup, do not pass go, do not collect $200
+ */
+ next0 = 0;
+ }
+ else
+ {
+ ge0 = NULL;
+ next0 = 0;
+ ASSERT (0);
+ }
+
+ if (PREDICT_TRUE (NULL != ge0))
+ sclass0 = ge0->ge_fwd.gef_sclass;
+ else
+ sclass0 = SCLASS_INVALID;
+ }
+
+ vnet_buffer2 (b0)->gbp.sclass = sclass0;
+
+ if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ gbp_classify_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sclass = sclass0;
+ }
+
+ 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);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (gbp_src_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_classify_inline (vm, node, frame,
+ GBP_SRC_CLASSIFY_PORT, DPO_PROTO_ETHERNET));
+}
+
+VLIB_NODE_FN (gbp_null_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_classify_inline (vm, node, frame,
+ GBP_SRC_CLASSIFY_NULL, DPO_PROTO_ETHERNET));
+}
+
+VLIB_NODE_FN (gbp_ip4_src_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_classify_inline (vm, node, frame,
+ GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP4));
+}
+
+VLIB_NODE_FN (gbp_ip6_src_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_classify_inline (vm, node, frame,
+ GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP6));
+}
+
+
+/* packet trace format function */
+static u8 *
+format_gbp_classify_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 *);
+ gbp_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *);
+
+ s = format (s, "sclass:%d", t->sclass);
+
+ return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_null_classify_node) = {
+ .name = "gbp-null-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 0,
+};
+
+VLIB_REGISTER_NODE (gbp_src_classify_node) = {
+ .name = "gbp-src-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 0,
+};
+
+VLIB_REGISTER_NODE (gbp_ip4_src_classify_node) = {
+ .name = "ip4-gbp-src-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "ip4-lookup"
+ },
+};
+
+VLIB_REGISTER_NODE (gbp_ip6_src_classify_node) = {
+ .name = "ip6-gbp-src-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "ip6-lookup"
+ },
+};
+
+VNET_FEATURE_INIT (gbp_ip4_src_classify_feat_node, static) =
+{
+ .arc_name = "ip4-unicast",
+ .node_name = "ip4-gbp-src-classify",
+ .runs_before = VNET_FEATURES ("nat44-out2in"),
+};
+VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) =
+{
+ .arc_name = "ip6-unicast",
+ .node_name = "ip6-gbp-src-classify",
+ .runs_before = VNET_FEATURES ("nat66-out2in"),
+};
+
+/* *INDENT-ON* */
+
+typedef enum gbp_lpm_classify_next_t_
+{
+ GPB_LPM_CLASSIFY_DROP,
+} gbp_lpm_classify_next_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_lpm_classify_trace_t_
+{
+ sclass_t sclass;
+ index_t lbi;
+ ip46_address_t src;
+} gbp_lpm_classify_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_gbp_lpm_classify_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 *);
+ gbp_lpm_classify_trace_t *t = va_arg (*args, gbp_lpm_classify_trace_t *);
+
+ s = format (s, "sclass:%d lb:%d src:%U",
+ t->sclass, t->lbi, format_ip46_address, &t->src, IP46_TYPE_ANY);
+
+ return s;
+}
+
+enum gbp_lpm_type
+{
+ GBP_LPM_RECIRC,
+ GBP_LPM_EPG,
+ GBP_LPM_ANON
+};
+
+/*
+ * Determine the SRC EPG from a LPM
+ */
+always_inline uword
+gbp_lpm_classify_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame,
+ const dpo_proto_t dproto,
+ const enum gbp_lpm_type type)
+{
+ gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
+ u32 n_left_from, *from, *to_next;
+ u32 next_index;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0, sw_if_index0, fib_index0, lbi0;
+ const gbp_endpoint_t *ge0, *ge_lpm0;
+ gbp_lpm_classify_next_t next0;
+ const ethernet_header_t *eh0;
+ const gbp_policy_dpo_t *gpd0;
+ const ip4_address_t *ip4_0;
+ const ip6_address_t *ip6_0;
+ const gbp_recirc_t *gr0;
+ vlib_buffer_t *b0;
+ sclass_t sclass0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+ ip4_0 = NULL;
+ ip6_0 = NULL;
+ next0 = GPB_LPM_CLASSIFY_DROP;
+
+ lbi0 = ~0;
+ eh0 = NULL;
+ b0 = vlib_get_buffer (vm, bi0);
+
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;
+
+ if (DPO_PROTO_IP4 == dproto)
+ ip4_0 =
+ &((ip4_header_t *) vlib_buffer_get_current (b0))->src_address;
+ else if (DPO_PROTO_IP6 == dproto)
+ ip6_0 =
+ &((ip6_header_t *) vlib_buffer_get_current (b0))->src_address;
+ else if (DPO_PROTO_ETHERNET == dproto)
+ {
+ eh0 = vlib_buffer_get_current (b0);
+ gbp_classify_get_ip_address (eh0, &ip4_0, &ip6_0,
+ GBP_CLASSIFY_GET_IP_SRC);
+ }
+
+ if (GBP_LPM_RECIRC == type)
+ {
+ gr0 = gbp_recirc_get (sw_if_index0);
+ fib_index0 = gr0->gr_fib_index[dproto];
+ ge0 = NULL;
+
+ vnet_feature_next (&next0, b0);
+ }
+ else
+ {
+ if (NULL == eh0)
+ {
+ /* packet should be l2 */
+ sclass0 = SCLASS_INVALID;
+ goto trace;
+ }
+
+ if (GBP_LPM_ANON == type)
+ {
+ /*
+ * anonymous LPM classification: only honour LPM as no EP
+ * were programmed
+ */
+ gbp_ext_itf_t *gei = gbp_ext_itf_get (sw_if_index0);
+ if (ip4_0)
+ fib_index0 = gei->gx_fib_index[DPO_PROTO_IP4];
+ else if (ip6_0)
+ fib_index0 = gei->gx_fib_index[DPO_PROTO_IP6];
+ else
+ {
+ /* not IP so no LPM classify possible */
+ sclass0 = SCLASS_INVALID;
+ next0 = GPB_LPM_CLASSIFY_DROP;
+ goto trace;
+ }
+ next0 = vnet_l2_feature_next
+ (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM_ANON],
+ L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY);
+ }
+ else
+ {
+ /*
+ * not an anonymous LPM classification: check it comes from
+ * an EP, and use EP RD info
+ */
+ ge0 = gbp_endpoint_find_mac (eh0->src_address,
+ vnet_buffer (b0)->l2.bd_index);
+
+ if (NULL == ge0)
+ {
+ /* packet must have come from an EP's mac */
+ sclass0 = SCLASS_INVALID;
+ goto trace;
+ }
+
+ fib_index0 = ge0->ge_fwd.gef_fib_index;
+
+ if (~0 == fib_index0)
+ {
+ sclass0 = SCLASS_INVALID;
+ goto trace;
+ }
+
+ if (ip4_0)
+ {
+ ge_lpm0 = gbp_endpoint_find_ip4 (ip4_0, fib_index0);
+ }
+ else if (ip6_0)
+ {
+ ge_lpm0 = gbp_endpoint_find_ip6 (ip6_0, fib_index0);
+ }
+ else
+ {
+ ge_lpm0 = NULL;
+ }
+
+ next0 = vnet_l2_feature_next
+ (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM],
+ L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+
+ /*
+ * if we found the EP by IP lookup, it must be from the EP
+ * not a network behind it
+ */
+ if (NULL != ge_lpm0)
+ {
+ if (PREDICT_FALSE (ge0 != ge_lpm0))
+ {
+ /* an EP spoofing another EP */
+ sclass0 = SCLASS_INVALID;
+ next0 = GPB_LPM_CLASSIFY_DROP;
+ }
+ else
+ {
+ sclass0 = ge0->ge_fwd.gef_sclass;
+ }
+ goto trace;
+ }
+ }
+ }
+
+ gpd0 = gbp_classify_get_gpd (ip4_0, ip6_0, fib_index0);
+ if (0 == gpd0)
+ {
+ /* could not classify => drop */
+ sclass0 = SCLASS_INVALID;
+ next0 = GPB_LPM_CLASSIFY_DROP;
+ goto trace;
+ }
+
+ sclass0 = gpd0->gpd_sclass;
+
+ /* all packets from an external network should not be learned by the
+ * reciever. so set the Do-not-learn bit here */
+ vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_D;
+
+ trace:
+ vnet_buffer2 (b0)->gbp.sclass = sclass0;
+
+ if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ gbp_lpm_classify_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sclass = sclass0;
+ t->lbi = lbi0;
+ if (ip4_0)
+ t->src.ip4 = *ip4_0;
+ if (ip6_0)
+ t->src.ip6 = *ip6_0;
+ }
+
+ 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);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (gbp_ip4_lpm_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_lpm_classify_inline
+ (vm, node, frame, DPO_PROTO_IP4, GBP_LPM_RECIRC));
+}
+
+VLIB_NODE_FN (gbp_ip6_lpm_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_lpm_classify_inline
+ (vm, node, frame, DPO_PROTO_IP6, GBP_LPM_RECIRC));
+}
+
+VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_lpm_classify_inline
+ (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_EPG));
+}
+
+VLIB_NODE_FN (gbp_l2_lpm_anon_classify_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_lpm_classify_inline
+ (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_ANON));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
+ .name = "ip4-gbp-lpm-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_lpm_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [GPB_LPM_CLASSIFY_DROP] = "ip4-drop"
+ },
+};
+
+VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
+ .name = "ip6-gbp-lpm-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_lpm_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [GPB_LPM_CLASSIFY_DROP] = "ip6-drop"
+ },
+};
+
+VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = {
+ .name = "l2-gbp-lpm-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_lpm_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [GPB_LPM_CLASSIFY_DROP] = "error-drop"
+ },
+};
+
+VLIB_REGISTER_NODE (gbp_l2_lpm_anon_classify_node) = {
+ .name = "l2-gbp-lpm-anon-classify",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_lpm_classify_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [GPB_LPM_CLASSIFY_DROP] = "error-drop"
+ },
+};
+
+VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
+{
+ .arc_name = "ip4-unicast",
+ .node_name = "ip4-gbp-lpm-classify",
+ .runs_before = VNET_FEATURES ("nat44-out2in"),
+};
+VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) =
+{
+ .arc_name = "ip6-unicast",
+ .node_name = "ip6-gbp-lpm-classify",
+ .runs_before = VNET_FEATURES ("nat66-out2in"),
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_contract.c b/extras/deprecated/plugins/gbp/gbp_contract.c
new file mode 100644
index 00000000000..dd433f28a84
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_contract.c
@@ -0,0 +1,819 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_contract.h>
+
+#include <vnet/dpo/load_balance.h>
+#include <vnet/dpo/drop_dpo.h>
+
+char *gbp_contract_error_strings[] = {
+#define _(sym,string) string,
+ foreach_gbp_contract_error
+#undef _
+};
+
+/**
+ * Single contract DB instance
+ */
+gbp_contract_db_t gbp_contract_db;
+
+gbp_contract_t *gbp_contract_pool;
+
+vlib_log_class_t gc_logger;
+
+fib_node_type_t gbp_next_hop_fib_type;
+
+gbp_rule_t *gbp_rule_pool;
+gbp_next_hop_t *gbp_next_hop_pool;
+
+#define GBP_CONTRACT_DBG(...) \
+ vlib_log_notice (gc_logger, __VA_ARGS__);
+
+/* Adjacency packet/byte counters indexed by adjacency index. */
+vlib_combined_counter_main_t gbp_contract_permit_counters = {
+ .name = "gbp-contracts-permit",
+ .stat_segment_name = "/net/gbp/contract/permit",
+};
+
+vlib_combined_counter_main_t gbp_contract_drop_counters = {
+ .name = "gbp-contracts-drop",
+ .stat_segment_name = "/net/gbp/contract/drop",
+};
+
+index_t
+gbp_rule_alloc (gbp_rule_action_t action,
+ gbp_hash_mode_t hash_mode, index_t * nhs)
+{
+ gbp_rule_t *gu;
+
+ pool_get_zero (gbp_rule_pool, gu);
+
+ gu->gu_hash_mode = hash_mode;
+ gu->gu_nhs = nhs;
+ gu->gu_action = action;
+
+ return (gu - gbp_rule_pool);
+}
+
+void
+gbp_rule_free (index_t gui)
+{
+ pool_put_index (gbp_rule_pool, gui);
+}
+
+index_t
+gbp_next_hop_alloc (const ip46_address_t * ip,
+ index_t grd, const mac_address_t * mac, index_t gbd)
+{
+ fib_protocol_t fproto;
+ gbp_next_hop_t *gnh;
+
+ pool_get_zero (gbp_next_hop_pool, gnh);
+
+ fib_node_init (&gnh->gnh_node, gbp_next_hop_fib_type);
+
+ ip46_address_copy (&gnh->gnh_ip, ip);
+ mac_address_copy (&gnh->gnh_mac, mac);
+
+ gnh->gnh_rd = grd;
+ gnh->gnh_bd = gbd;
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto) gnh->gnh_ai[fproto] = INDEX_INVALID;
+
+ return (gnh - gbp_next_hop_pool);
+}
+
+static inline gbp_next_hop_t *
+gbp_next_hop_get (index_t gui)
+{
+ return (pool_elt_at_index (gbp_next_hop_pool, gui));
+}
+
+static void
+gbp_contract_rules_free (index_t * rules)
+{
+ index_t *gui, *gnhi;
+
+ vec_foreach (gui, rules)
+ {
+ gbp_policy_node_t pnode;
+ fib_protocol_t fproto;
+ gbp_next_hop_t *gnh;
+ gbp_rule_t *gu;
+
+ gu = gbp_rule_get (*gui);
+
+ FOR_EACH_GBP_POLICY_NODE (pnode)
+ {
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ dpo_reset (&gu->gu_dpo[pnode][fproto]);
+ dpo_reset (&gu->gu_dpo[pnode][fproto]);
+ }
+ }
+
+ vec_foreach (gnhi, gu->gu_nhs)
+ {
+ fib_protocol_t fproto;
+
+ gnh = gbp_next_hop_get (*gnhi);
+ gbp_bridge_domain_unlock (gnh->gnh_bd);
+ gbp_route_domain_unlock (gnh->gnh_rd);
+ gbp_endpoint_child_remove (gnh->gnh_ge, gnh->gnh_sibling);
+ gbp_endpoint_unlock (GBP_ENDPOINT_SRC_RR, gnh->gnh_ge);
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ adj_unlock (gnh->gnh_ai[fproto]);
+ }
+ }
+
+ gbp_rule_free (*gui);
+ }
+ vec_free (rules);
+}
+
+static u8 *
+format_gbp_next_hop (u8 * s, va_list * args)
+{
+ index_t gnhi = va_arg (*args, index_t);
+ gbp_next_hop_t *gnh;
+
+ gnh = gbp_next_hop_get (gnhi);
+
+ s = format (s, "%U, %U, %U EP:%d",
+ format_mac_address_t, &gnh->gnh_mac,
+ format_gbp_bridge_domain, gnh->gnh_bd,
+ format_ip46_address, &gnh->gnh_ip, IP46_TYPE_ANY, gnh->gnh_ge);
+
+ return (s);
+}
+
+u8 *
+format_gbp_rule_action (u8 * s, va_list * args)
+{
+ gbp_rule_action_t action = va_arg (*args, gbp_rule_action_t);
+
+ switch (action)
+ {
+#define _(v,a) case GBP_RULE_##v: return (format (s, "%s", a));
+ foreach_gbp_rule_action
+#undef _
+ }
+
+ return (format (s, "unknown"));
+}
+
+static u8 *
+format_gbp_hash_mode (u8 * s, va_list * args)
+{
+ gbp_hash_mode_t hash_mode = va_arg (*args, gbp_hash_mode_t);
+
+ switch (hash_mode)
+ {
+#define _(v,a) case GBP_HASH_MODE_##v: return (format (s, "%s", a));
+ foreach_gbp_hash_mode
+#undef _
+ }
+
+ return (format (s, "unknown"));
+}
+
+static u8 *
+format_gbp_policy_node (u8 * s, va_list * args)
+{
+ gbp_policy_node_t action = va_arg (*args, gbp_policy_node_t);
+
+ switch (action)
+ {
+#define _(v,a) case GBP_POLICY_NODE_##v: return (format (s, "%s", a));
+ foreach_gbp_policy_node
+#undef _
+ }
+
+ return (format (s, "unknown"));
+}
+
+static u8 *
+format_gbp_rule (u8 * s, va_list * args)
+{
+ index_t gui = va_arg (*args, index_t);
+ gbp_policy_node_t pnode;
+ fib_protocol_t fproto;
+ gbp_rule_t *gu;
+ index_t *gnhi;
+
+ gu = gbp_rule_get (gui);
+ s = format (s, "%U", format_gbp_rule_action, gu->gu_action);
+
+ switch (gu->gu_action)
+ {
+ case GBP_RULE_PERMIT:
+ case GBP_RULE_DENY:
+ return (s);
+ case GBP_RULE_REDIRECT:
+ s = format (s, ", %U", format_gbp_hash_mode, gu->gu_hash_mode);
+ break;
+ }
+
+ vec_foreach (gnhi, gu->gu_nhs)
+ {
+ s = format (s, "\n [%U]", format_gbp_next_hop, *gnhi);
+ }
+
+ FOR_EACH_GBP_POLICY_NODE (pnode)
+ {
+ s = format (s, "\n policy-%U", format_gbp_policy_node, pnode);
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ if (dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
+ {
+ s =
+ format (s, "\n %U", format_dpo_id,
+ &gu->gu_dpo[pnode][fproto], 8);
+ }
+ }
+ }
+
+ return (s);
+}
+
+static void
+gbp_contract_mk_adj (gbp_next_hop_t * gnh, fib_protocol_t fproto)
+{
+ ethernet_header_t *eth;
+ gbp_endpoint_t *ge;
+ index_t old_ai;
+ u8 *rewrite;
+
+ old_ai = gnh->gnh_ai[fproto];
+ rewrite = NULL;
+ vec_validate (rewrite, sizeof (*eth) - 1);
+ eth = (ethernet_header_t *) rewrite;
+
+ GBP_CONTRACT_DBG ("...mk-adj: %U", format_gbp_next_hop,
+ gnh - gbp_next_hop_pool);
+
+ ge = gbp_endpoint_get (gnh->gnh_ge);
+
+ eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
+ ETHERNET_TYPE_IP4 : ETHERNET_TYPE_IP6));
+ mac_address_to_bytes (gbp_route_domain_get_local_mac (), eth->src_address);
+ mac_address_to_bytes (&gnh->gnh_mac, eth->dst_address);
+
+ gnh->gnh_ai[fproto] =
+ adj_nbr_add_or_lock_w_rewrite (fproto,
+ fib_proto_to_link (fproto),
+ &gnh->gnh_ip,
+ gbp_itf_get_sw_if_index (ge->
+ ge_fwd.gef_itf),
+ rewrite);
+
+ adj_unlock (old_ai);
+}
+
+static flow_hash_config_t
+gbp_contract_mk_lb_hp (gbp_hash_mode_t gu_hash_mode)
+{
+ switch (gu_hash_mode)
+ {
+ case GBP_HASH_MODE_SRC_IP:
+ return IP_FLOW_HASH_SRC_ADDR;
+ case GBP_HASH_MODE_DST_IP:
+ return IP_FLOW_HASH_DST_ADDR;
+ case GBP_HASH_MODE_SYMMETRIC:
+ return (IP_FLOW_HASH_SRC_ADDR | IP_FLOW_HASH_DST_ADDR |
+ IP_FLOW_HASH_PROTO | IP_FLOW_HASH_SYMMETRIC);
+ }
+
+ return 0;
+}
+
+static void
+gbp_contract_mk_lb (index_t gui, fib_protocol_t fproto)
+{
+ load_balance_path_t *paths = NULL;
+ gbp_policy_node_t pnode;
+ gbp_next_hop_t *gnh;
+ dpo_proto_t dproto;
+ gbp_rule_t *gu;
+ u32 ii;
+
+ u32 policy_nodes[] = {
+ [GBP_POLICY_NODE_L2] = gbp_policy_port_node.index,
+ [GBP_POLICY_NODE_IP4] = ip4_gbp_policy_dpo_node.index,
+ [GBP_POLICY_NODE_IP6] = ip6_gbp_policy_dpo_node.index,
+ };
+
+ GBP_CONTRACT_DBG ("..mk-lb: %U", format_gbp_rule, gui);
+
+ gu = gbp_rule_get (gui);
+ dproto = fib_proto_to_dpo (fproto);
+
+ if (GBP_RULE_REDIRECT != gu->gu_action)
+ return;
+
+ vec_foreach_index (ii, gu->gu_nhs)
+ {
+ gnh = gbp_next_hop_get (gu->gu_nhs[ii]);
+
+ gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP4);
+ gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP6);
+ }
+
+ FOR_EACH_GBP_POLICY_NODE (pnode)
+ {
+ vec_validate (paths, vec_len (gu->gu_nhs) - 1);
+
+ vec_foreach_index (ii, gu->gu_nhs)
+ {
+ gnh = gbp_next_hop_get (gu->gu_nhs[ii]);
+
+ paths[ii].path_index = FIB_NODE_INDEX_INVALID;
+ paths[ii].path_weight = 1;
+ dpo_set (&paths[ii].path_dpo, DPO_ADJACENCY,
+ dproto, gnh->gnh_ai[fproto]);
+ }
+
+ if (!dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
+ {
+ dpo_id_t dpo = DPO_INVALID;
+
+ dpo_set (&dpo, DPO_LOAD_BALANCE, dproto,
+ load_balance_create (vec_len (paths),
+ dproto,
+ gbp_contract_mk_lb_hp
+ (gu->gu_hash_mode)));
+ dpo_stack_from_node (policy_nodes[pnode], &gu->gu_dpo[pnode][fproto],
+ &dpo);
+ dpo_reset (&dpo);
+ }
+
+ load_balance_multipath_update (&gu->gu_dpo[pnode][fproto],
+ paths, LOAD_BALANCE_FLAG_NONE);
+ vec_free (paths);
+ }
+}
+
+static void
+gbp_contract_mk_one_lb (index_t gui)
+{
+ gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP4);
+ gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP6);
+}
+
+static int
+gbp_contract_next_hop_resolve (index_t gui, index_t gnhi)
+{
+ gbp_bridge_domain_t *gbd;
+ gbp_next_hop_t *gnh;
+ ip46_address_t *ips;
+ int rv;
+
+ ips = NULL;
+ gnh = gbp_next_hop_get (gnhi);
+ gbd = gbp_bridge_domain_get (gnh->gnh_bd);
+
+ gnh->gnh_gu = gui;
+ vec_add1 (ips, gnh->gnh_ip);
+
+ /*
+ * source the endpoint this contract needs to forward via.
+ * give ofrwarding details via the spine proxy. if this EP is known
+ * to us, then since we source here with a low priority, the learned
+ * info will take precedenc.
+ */
+ rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_RR,
+ gbd->gb_uu_fwd_sw_if_index,
+ ips,
+ &gnh->gnh_mac,
+ gnh->gnh_bd, gnh->gnh_rd, SCLASS_INVALID,
+ GBP_ENDPOINT_FLAG_NONE, NULL, NULL,
+ &gnh->gnh_ge);
+
+ if (0 == rv)
+ {
+ gnh->gnh_sibling = gbp_endpoint_child_add (gnh->gnh_ge,
+ gbp_next_hop_fib_type, gnhi);
+ }
+
+ GBP_CONTRACT_DBG ("..resolve: %d: %d: %U", gui, gnhi, format_gbp_next_hop,
+ gnhi);
+
+ vec_free (ips);
+ return (rv);
+}
+
+static void
+gbp_contract_rule_resolve (index_t gui)
+{
+ gbp_rule_t *gu;
+ index_t *gnhi;
+
+ gu = gbp_rule_get (gui);
+
+ GBP_CONTRACT_DBG ("..resolve: %U", format_gbp_rule, gui);
+
+ vec_foreach (gnhi, gu->gu_nhs)
+ {
+ gbp_contract_next_hop_resolve (gui, *gnhi);
+ }
+}
+
+static void
+gbp_contract_resolve (index_t * guis)
+{
+ index_t *gui;
+
+ vec_foreach (gui, guis)
+ {
+ gbp_contract_rule_resolve (*gui);
+ }
+}
+
+static void
+gbp_contract_mk_lbs (index_t * guis)
+{
+ index_t *gui;
+
+ vec_foreach (gui, guis)
+ {
+ gbp_contract_mk_one_lb (*gui);
+ }
+}
+
+int
+gbp_contract_update (gbp_scope_t scope,
+ sclass_t sclass,
+ sclass_t dclass,
+ u32 acl_index,
+ index_t * rules,
+ u16 * allowed_ethertypes, u32 * stats_index)
+{
+ gbp_main_t *gm = &gbp_main;
+ u32 *acl_vec = NULL;
+ gbp_contract_t *gc;
+ index_t gci;
+ uword *p;
+
+ gbp_contract_key_t key = {
+ .gck_scope = scope,
+ .gck_src = sclass,
+ .gck_dst = dclass,
+ };
+
+ if (~0 == gm->gbp_acl_user_id)
+ {
+ acl_plugin_exports_init (&gm->acl_plugin);
+ gm->gbp_acl_user_id =
+ gm->acl_plugin.register_user_module ("GBP ACL", "src-epg", "dst-epg");
+ }
+
+ p = hash_get (gbp_contract_db.gc_hash, key.as_u64);
+ if (p != NULL)
+ {
+ gci = p[0];
+ gc = gbp_contract_get (gci);
+ gbp_contract_rules_free (gc->gc_rules);
+ gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
+ gc->gc_rules = NULL;
+ vec_free (gc->gc_allowed_ethertypes);
+ }
+ else
+ {
+ pool_get_zero (gbp_contract_pool, gc);
+ gc->gc_key = key;
+ gci = gc - gbp_contract_pool;
+ hash_set (gbp_contract_db.gc_hash, key.as_u64, gci);
+
+ vlib_validate_combined_counter (&gbp_contract_drop_counters, gci);
+ vlib_zero_combined_counter (&gbp_contract_drop_counters, gci);
+ vlib_validate_combined_counter (&gbp_contract_permit_counters, gci);
+ vlib_zero_combined_counter (&gbp_contract_permit_counters, gci);
+ }
+
+ GBP_CONTRACT_DBG ("update: %U", format_gbp_contract, gci);
+
+ gc->gc_rules = rules;
+ gc->gc_allowed_ethertypes = allowed_ethertypes;
+ gbp_contract_resolve (gc->gc_rules);
+ gbp_contract_mk_lbs (gc->gc_rules);
+
+ gc->gc_acl_index = acl_index;
+ gc->gc_lc_index =
+ gm->acl_plugin.get_lookup_context_index (gm->gbp_acl_user_id,
+ sclass, dclass);
+
+ vec_add1 (acl_vec, gc->gc_acl_index);
+ gm->acl_plugin.set_acl_vec_for_context (gc->gc_lc_index, acl_vec);
+ vec_free (acl_vec);
+
+ *stats_index = gci;
+
+ return (0);
+}
+
+int
+gbp_contract_delete (gbp_scope_t scope, sclass_t sclass, sclass_t dclass)
+{
+ gbp_contract_key_t key = {
+ .gck_scope = scope,
+ .gck_src = sclass,
+ .gck_dst = dclass,
+ };
+ gbp_contract_t *gc;
+ uword *p;
+
+ p = hash_get (gbp_contract_db.gc_hash, key.as_u64);
+ if (p != NULL)
+ {
+ gc = gbp_contract_get (p[0]);
+
+ gbp_contract_rules_free (gc->gc_rules);
+ gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
+ vec_free (gc->gc_allowed_ethertypes);
+
+ hash_unset (gbp_contract_db.gc_hash, key.as_u64);
+ pool_put (gbp_contract_pool, gc);
+
+ return (0);
+ }
+
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+void
+gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
+{
+ gbp_contract_t *gc;
+
+ /* *INDENT-OFF* */
+ pool_foreach (gc, gbp_contract_pool)
+ {
+ if (!cb(gc, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_contract_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ sclass_t sclass = SCLASS_INVALID, dclass = SCLASS_INVALID;
+ u32 acl_index = ~0, stats_index, scope;
+ u8 add = 1;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "add"))
+ add = 1;
+ else if (unformat (input, "del"))
+ add = 0;
+ else if (unformat (input, "scope %d", &scope))
+ ;
+ else if (unformat (input, "sclass %d", &sclass))
+ ;
+ else if (unformat (input, "dclass %d", &dclass))
+ ;
+ else if (unformat (input, "acl-index %d", &acl_index))
+ ;
+ else
+ break;
+ }
+
+ if (SCLASS_INVALID == sclass)
+ return clib_error_return (0, "Source EPG-ID must be specified");
+ if (SCLASS_INVALID == dclass)
+ return clib_error_return (0, "Destination EPG-ID must be specified");
+
+ if (add)
+ {
+ gbp_contract_update (scope, sclass, dclass, acl_index,
+ NULL, NULL, &stats_index);
+ }
+ else
+ {
+ gbp_contract_delete (scope, sclass, dclass);
+ }
+
+ return (NULL);
+}
+
+/*?
+ * Configure a GBP Contract
+ *
+ * @cliexpar
+ * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_contract_cli_node, static) =
+{
+ .path = "gbp contract",
+ .short_help =
+ "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
+ .function = gbp_contract_cli,
+};
+/* *INDENT-ON* */
+
+static u8 *
+format_gbp_contract_key (u8 * s, va_list * args)
+{
+ gbp_contract_key_t *gck = va_arg (*args, gbp_contract_key_t *);
+
+ s = format (s, "{%d,%d,%d}", gck->gck_scope, gck->gck_src, gck->gck_dst);
+
+ return (s);
+}
+
+u8 *
+format_gbp_contract (u8 * s, va_list * args)
+{
+ index_t gci = va_arg (*args, index_t);
+ vlib_counter_t counts;
+ gbp_contract_t *gc;
+ index_t *gui;
+ u16 *et;
+
+ gc = gbp_contract_get (gci);
+
+ s = format (s, "[%d] %U: acl-index:%d",
+ gci, format_gbp_contract_key, &gc->gc_key, gc->gc_acl_index);
+
+ s = format (s, "\n rules:");
+ vec_foreach (gui, gc->gc_rules)
+ {
+ s = format (s, "\n %d: %U", *gui, format_gbp_rule, *gui);
+ }
+
+ s = format (s, "\n allowed-ethertypes:");
+ s = format (s, "\n [");
+ vec_foreach (et, gc->gc_allowed_ethertypes)
+ {
+ int host_et = clib_net_to_host_u16 (*et);
+ if (0 != host_et)
+ s = format (s, "0x%x, ", host_et);
+ }
+ s = format (s, "]");
+
+ s = format (s, "\n stats:");
+ vlib_get_combined_counter (&gbp_contract_drop_counters, gci, &counts);
+ s = format (s, "\n drop:[%Ld:%Ld]", counts.packets, counts.bytes);
+ vlib_get_combined_counter (&gbp_contract_permit_counters, gci, &counts);
+ s = format (s, "\n permit:[%Ld:%Ld]", counts.packets, counts.bytes);
+
+ s = format (s, "]");
+
+ return (s);
+}
+
+static clib_error_t *
+gbp_contract_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ gbp_contract_t *gc;
+ u32 src, dst;
+ index_t gci;
+
+ src = dst = SCLASS_INVALID;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "src %d", &src))
+ ;
+ else if (unformat (input, "dst %d", &dst))
+ ;
+ else
+ break;
+ }
+
+ vlib_cli_output (vm, "Contracts:");
+
+ /* *INDENT-OFF* */
+ pool_foreach (gc, gbp_contract_pool)
+ {
+ gci = gc - gbp_contract_pool;
+
+ if (SCLASS_INVALID != src && SCLASS_INVALID != dst)
+ {
+ if (gc->gc_key.gck_src == src &&
+ gc->gc_key.gck_dst == dst)
+ vlib_cli_output (vm, " %U", format_gbp_contract, gci);
+ }
+ else if (SCLASS_INVALID != src)
+ {
+ if (gc->gc_key.gck_src == src)
+ vlib_cli_output (vm, " %U", format_gbp_contract, gci);
+ }
+ else if (SCLASS_INVALID != dst)
+ {
+ if (gc->gc_key.gck_dst == dst)
+ vlib_cli_output (vm, " %U", format_gbp_contract, gci);
+ }
+ else
+ vlib_cli_output (vm, " %U", format_gbp_contract, gci);
+ }
+ /* *INDENT-ON* */
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Contracts
+ *
+ * @cliexpar
+ * @cliexstart{show gbp contract}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
+ .path = "show gbp contract",
+ .short_help = "show gbp contract [src <SRC>] [dst <DST>]\n",
+ .function = gbp_contract_show,
+};
+/* *INDENT-ON* */
+
+static fib_node_t *
+gbp_next_hop_get_node (fib_node_index_t index)
+{
+ gbp_next_hop_t *gnh;
+
+ gnh = gbp_next_hop_get (index);
+
+ return (&gnh->gnh_node);
+}
+
+static void
+gbp_next_hop_last_lock_gone (fib_node_t * node)
+{
+ ASSERT (0);
+}
+
+static gbp_next_hop_t *
+gbp_next_hop_from_fib_node (fib_node_t * node)
+{
+ ASSERT (gbp_next_hop_fib_type == node->fn_type);
+ return ((gbp_next_hop_t *) node);
+}
+
+static fib_node_back_walk_rc_t
+gbp_next_hop_back_walk_notify (fib_node_t * node,
+ fib_node_back_walk_ctx_t * ctx)
+{
+ gbp_next_hop_t *gnh;
+
+ gnh = gbp_next_hop_from_fib_node (node);
+
+ gbp_contract_mk_one_lb (gnh->gnh_gu);
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/*
+ * The FIB path's graph node virtual function table
+ */
+static const fib_node_vft_t gbp_next_hop_vft = {
+ .fnv_get = gbp_next_hop_get_node,
+ .fnv_last_lock = gbp_next_hop_last_lock_gone,
+ .fnv_back_walk = gbp_next_hop_back_walk_notify,
+ // .fnv_mem_show = fib_path_memory_show,
+};
+
+static clib_error_t *
+gbp_contract_init (vlib_main_t * vm)
+{
+ gc_logger = vlib_log_register_class ("gbp", "con");
+ gbp_next_hop_fib_type = fib_node_register_new_type (&gbp_next_hop_vft);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_contract_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_contract.h b/extras/deprecated/plugins/gbp/gbp_contract.h
new file mode 100644
index 00000000000..1e74db60116
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_contract.h
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_CONTRACT_H__
+#define __GBP_CONTRACT_H__
+
+#include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_types.h>
+
+#define foreach_gbp_contract_error \
+ _(ALLOW_NO_SCLASS, "allow-no-sclass") \
+ _(ALLOW_INTRA, "allow-intra-sclass") \
+ _(ALLOW_A_BIT, "allow-a-bit-set") \
+ _(ALLOW_SCLASS_1, "allow-sclass-1") \
+ _(ALLOW_CONTRACT, "allow-contract") \
+ _(DROP_CONTRACT, "drop-contract") \
+ _(DROP_ETHER_TYPE, "drop-ether-type") \
+ _(DROP_NO_CONTRACT, "drop-no-contract") \
+ _(DROP_NO_DCLASS, "drop-no-dclass") \
+ _(DROP_NO_RULE, "drop-no-rule")
+
+typedef enum
+{
+#define _(sym,str) GBP_CONTRACT_ERROR_##sym,
+ foreach_gbp_contract_error
+#undef _
+ GBP_CONTRACT_N_ERROR,
+#define GBP_CONTRACT_N_ERROR GBP_CONTRACT_N_ERROR
+} gbp_contract_error_t;
+
+extern char *gbp_contract_error_strings[GBP_CONTRACT_N_ERROR];
+
+/**
+ * The key for an Contract
+ */
+typedef struct gbp_contract_key_t_
+{
+ union
+ {
+ struct
+ {
+ gbp_scope_t gck_scope;
+ /**
+ * source and destination EPGs for which the ACL applies
+ */
+ sclass_t gck_src;
+ sclass_t gck_dst;
+ };
+ u64 as_u64;
+ };
+} gbp_contract_key_t;
+
+typedef struct gbp_next_hop_t_
+{
+ fib_node_t gnh_node;
+ ip46_address_t gnh_ip;
+ mac_address_t gnh_mac;
+ index_t gnh_gu;
+ index_t gnh_bd;
+ index_t gnh_rd;
+ u32 gnh_ge;
+ u32 gnh_sibling;
+ index_t gnh_ai[FIB_PROTOCOL_IP_MAX];
+} gbp_next_hop_t;
+
+#define foreach_gbp_hash_mode \
+ _(SRC_IP, "src-ip") \
+ _(DST_IP, "dst-ip") \
+ _(SYMMETRIC, "symmetric")
+
+typedef enum gbp_hash_mode_t_
+{
+#define _(v,s) GBP_HASH_MODE_##v,
+ foreach_gbp_hash_mode
+#undef _
+} gbp_hash_mode_t;
+
+#define foreach_gbp_rule_action \
+ _(PERMIT, "permit") \
+ _(DENY, "deny") \
+ _(REDIRECT, "redirect")
+
+typedef enum gbp_rule_action_t_
+{
+#define _(v,s) GBP_RULE_##v,
+ foreach_gbp_rule_action
+#undef _
+} gbp_rule_action_t;
+
+#define foreach_gbp_policy_node \
+ _(L2, "L2") \
+ _(IP4, "ip4") \
+ _(IP6, "ip6")
+
+typedef enum gbp_policy_node_t_
+{
+#define _(v,s) GBP_POLICY_NODE_##v,
+ foreach_gbp_policy_node
+#undef _
+} gbp_policy_node_t;
+#define GBP_POLICY_N_NODES (GBP_POLICY_NODE_IP6+1)
+
+#define FOR_EACH_GBP_POLICY_NODE(pnode) \
+ for (pnode = GBP_POLICY_NODE_L2; pnode < GBP_POLICY_N_NODES; pnode++)
+
+typedef struct gbp_rule_t_
+{
+ gbp_rule_action_t gu_action;
+ gbp_hash_mode_t gu_hash_mode;
+ index_t *gu_nhs;
+
+ /**
+ * DPO of the load-balance object used to redirect
+ */
+ dpo_id_t gu_dpo[GBP_POLICY_N_NODES][FIB_PROTOCOL_IP_MAX];
+} gbp_rule_t;
+
+/**
+ * A Group Based Policy Contract.
+ * Determines the ACL that applies to traffic pass between two endpoint groups
+ */
+typedef struct gbp_contract_t_
+{
+ /**
+ * source and destination EPGs
+ */
+ gbp_contract_key_t gc_key;
+
+ u32 gc_acl_index;
+ u32 gc_lc_index;
+
+ /**
+ * The ACL to apply for packets from the source to the destination EPG
+ */
+ index_t *gc_rules;
+
+ /**
+ * An ethertype whitelist
+ */
+ u16 *gc_allowed_ethertypes;
+} gbp_contract_t;
+
+/**
+ * EPG src,dst pair to ACL mapping table, aka contract DB
+ */
+typedef struct gbp_contract_db_t_
+{
+ /**
+ * We can form a u64 key from the pair, so use a simple hash table
+ */
+ uword *gc_hash;
+} gbp_contract_db_t;
+
+extern int gbp_contract_update (gbp_scope_t scope,
+ sclass_t sclass,
+ sclass_t dclass,
+ u32 acl_index,
+ index_t * rules,
+ u16 * allowed_ethertypes, u32 * stats_index);
+extern int gbp_contract_delete (gbp_scope_t scope, sclass_t sclass,
+ sclass_t dclass);
+
+extern index_t gbp_rule_alloc (gbp_rule_action_t action,
+ gbp_hash_mode_t hash_mode, index_t * nhs);
+extern void gbp_rule_free (index_t gui);
+extern index_t gbp_next_hop_alloc (const ip46_address_t * ip,
+ index_t grd,
+ const mac_address_t * mac, index_t gbd);
+
+typedef int (*gbp_contract_cb_t) (gbp_contract_t * gbpe, void *ctx);
+extern void gbp_contract_walk (gbp_contract_cb_t bgpe, void *ctx);
+
+extern u8 *format_gbp_rule_action (u8 * s, va_list * args);
+extern u8 *format_gbp_contract (u8 * s, va_list * args);
+
+/**
+ * DP functions and databases
+ */
+extern gbp_contract_db_t gbp_contract_db;
+
+always_inline index_t
+gbp_contract_find (gbp_contract_key_t * key)
+{
+ uword *p;
+
+ p = hash_get (gbp_contract_db.gc_hash, key->as_u64);
+
+ if (NULL != p)
+ return (p[0]);
+
+ return (INDEX_INVALID);
+}
+
+extern gbp_contract_t *gbp_contract_pool;
+
+always_inline gbp_contract_t *
+gbp_contract_get (index_t gci)
+{
+ return (pool_elt_at_index (gbp_contract_pool, gci));
+}
+
+extern gbp_rule_t *gbp_rule_pool;
+
+always_inline gbp_rule_t *
+gbp_rule_get (index_t gui)
+{
+ return (pool_elt_at_index (gbp_rule_pool, gui));
+}
+
+extern vlib_combined_counter_main_t gbp_contract_permit_counters;
+extern vlib_combined_counter_main_t gbp_contract_drop_counters;
+
+typedef enum
+{
+ GBP_CONTRACT_APPLY_L2,
+ GBP_CONTRACT_APPLY_IP4,
+ GBP_CONTRACT_APPLY_IP6,
+} gbp_contract_apply_type_t;
+
+static_always_inline gbp_rule_action_t
+gbp_contract_apply (vlib_main_t * vm, gbp_main_t * gm,
+ gbp_contract_key_t * key, vlib_buffer_t * b,
+ gbp_rule_t ** rule, u32 * intra, u32 * sclass1,
+ u32 * acl_match, u32 * rule_match,
+ gbp_contract_error_t * err,
+ gbp_contract_apply_type_t type)
+{
+ fa_5tuple_opaque_t fa_5tuple;
+ const gbp_contract_t *contract;
+ index_t contract_index;
+ u32 acl_pos, trace_bitmap;
+ u16 etype;
+ u8 ip6, action;
+
+ *rule = 0;
+ trace_bitmap = 0;
+
+ if (key->gck_src == key->gck_dst)
+ {
+ /* intra-epg allowed */
+ (*intra)++;
+ *err = GBP_CONTRACT_ERROR_ALLOW_INTRA;
+ return GBP_RULE_PERMIT;
+ }
+
+ if (1 == key->gck_src || 1 == key->gck_dst)
+ {
+ /* sclass 1 allowed */
+ (*sclass1)++;
+ *err = GBP_CONTRACT_ERROR_ALLOW_SCLASS_1;
+ return GBP_RULE_PERMIT;
+ }
+
+ /* look for contract */
+ contract_index = gbp_contract_find (key);
+ if (INDEX_INVALID == contract_index)
+ {
+ *err = GBP_CONTRACT_ERROR_DROP_NO_CONTRACT;
+ return GBP_RULE_DENY;
+ }
+
+ contract = gbp_contract_get (contract_index);
+
+ *err = GBP_CONTRACT_ERROR_DROP_CONTRACT;
+
+ switch (type)
+ {
+ case GBP_CONTRACT_APPLY_IP4:
+ ip6 = 0;
+ break;
+ case GBP_CONTRACT_APPLY_IP6:
+ ip6 = 1;
+ break;
+ case GBP_CONTRACT_APPLY_L2:
+ {
+ /* check ethertype */
+ etype =
+ ((u16 *) (vlib_buffer_get_current (b) +
+ vnet_buffer (b)->l2.l2_len))[-1];
+
+ if (~0 == vec_search (contract->gc_allowed_ethertypes, etype))
+ {
+ *err = GBP_CONTRACT_ERROR_DROP_ETHER_TYPE;
+ goto contract_deny;
+ }
+
+ switch (clib_net_to_host_u16 (etype))
+ {
+ case ETHERNET_TYPE_IP4:
+ ip6 = 0;
+ break;
+ case ETHERNET_TYPE_IP6:
+ ip6 = 1;
+ break;
+ default:
+ goto contract_deny;
+ }
+ }
+ break;
+ }
+
+ /* check ACL */
+ action = 0;
+ acl_plugin_fill_5tuple_inline (gm->acl_plugin.p_acl_main,
+ contract->gc_lc_index, b, ip6,
+ GBP_CONTRACT_APPLY_L2 != type /* input */ ,
+ GBP_CONTRACT_APPLY_L2 == type /* l2_path */ ,
+ &fa_5tuple);
+ acl_plugin_match_5tuple_inline (gm->acl_plugin.p_acl_main,
+ contract->gc_lc_index, &fa_5tuple, ip6,
+ &action, &acl_pos, acl_match, rule_match,
+ &trace_bitmap);
+ if (action <= 0)
+ goto contract_deny;
+
+ if (PREDICT_FALSE (*rule_match >= vec_len (contract->gc_rules)))
+ {
+ *err = GBP_CONTRACT_ERROR_DROP_NO_RULE;
+ goto contract_deny;
+ }
+
+ *rule = gbp_rule_get (contract->gc_rules[*rule_match]);
+ switch ((*rule)->gu_action)
+ {
+ case GBP_RULE_PERMIT:
+ case GBP_RULE_REDIRECT:
+ *err = GBP_CONTRACT_ERROR_ALLOW_CONTRACT;
+ vlib_increment_combined_counter (&gbp_contract_permit_counters,
+ vm->thread_index, contract_index, 1,
+ vlib_buffer_length_in_chain (vm, b));
+ return (*rule)->gu_action;
+ case GBP_RULE_DENY:
+ break;
+ }
+
+contract_deny:
+ vlib_increment_combined_counter (&gbp_contract_drop_counters,
+ vm->thread_index, contract_index, 1,
+ vlib_buffer_length_in_chain (vm, b));
+ return GBP_RULE_DENY;
+}
+
+#endif /* __GBP_CONTRACT_H__ */
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint.c b/extras/deprecated/plugins/gbp/gbp_endpoint.c
new file mode 100644
index 00000000000..2909b79772e
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_endpoint.c
@@ -0,0 +1,1595 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_itf.h>
+#include <plugins/gbp/gbp_scanner.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_vxlan.h>
+
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
+#include <vnet/l2/feat_bitmap.h>
+#include <vnet/l2/l2_fib.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip-neighbor/ip_neighbor.h>
+#include <vnet/ip-neighbor/ip4_neighbor.h>
+#include <vnet/ip-neighbor/ip6_neighbor.h>
+#include <vnet/fib/fib_walk.h>
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+
+static const char *gbp_endpoint_attr_names[] = GBP_ENDPOINT_ATTR_NAMES;
+
+/**
+ * EP DBs
+ */
+gbp_ep_db_t gbp_ep_db;
+
+static fib_source_t gbp_fib_source_hi;
+static fib_source_t gbp_fib_source_low;
+static fib_node_type_t gbp_endpoint_fib_type;
+static vlib_log_class_t gbp_ep_logger;
+
+#define GBP_ENDPOINT_DBG(...) \
+ vlib_log_debug (gbp_ep_logger, __VA_ARGS__);
+
+#define GBP_ENDPOINT_INFO(...) \
+ vlib_log_notice (gbp_ep_logger, __VA_ARGS__);
+
+/**
+ * Pool of GBP endpoints
+ */
+gbp_endpoint_t *gbp_endpoint_pool;
+
+/**
+ * A count of the number of dynamic entries
+ */
+static u32 gbp_n_learnt_endpoints;
+
+#define FOR_EACH_GBP_ENDPOINT_ATTR(_item) \
+ for (_item = GBP_ENDPOINT_ATTR_FIRST; \
+ _item < GBP_ENDPOINT_ATTR_LAST; \
+ _item++)
+
+u8 *
+format_gbp_endpoint_flags (u8 * s, va_list * args)
+{
+ gbp_endpoint_attr_t attr;
+ gbp_endpoint_flags_t flags = va_arg (*args, gbp_endpoint_flags_t);
+
+ FOR_EACH_GBP_ENDPOINT_ATTR (attr)
+ {
+ if ((1 << attr) & flags)
+ {
+ s = format (s, "%s,", gbp_endpoint_attr_names[attr]);
+ }
+ }
+
+ return (s);
+}
+
+int
+gbp_endpoint_is_remote (const gbp_endpoint_t * ge)
+{
+ return (! !(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE));
+}
+
+int
+gbp_endpoint_is_local (const gbp_endpoint_t * ge)
+{
+ return (!(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE));
+}
+
+int
+gbp_endpoint_is_external (const gbp_endpoint_t * ge)
+{
+ return (! !(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_EXTERNAL));
+}
+
+int
+gbp_endpoint_is_learnt (const gbp_endpoint_t * ge)
+{
+ if (0 == vec_len (ge->ge_locs))
+ return 0;
+
+ /* DP is the highest source so if present it will be first */
+ return (ge->ge_locs[0].gel_src == GBP_ENDPOINT_SRC_DP);
+}
+
+static void
+gbp_endpoint_extract_key_mac_itf (const clib_bihash_kv_16_8_t * key,
+ mac_address_t * mac, u32 * sw_if_index)
+{
+ mac_address_from_u64 (mac, key->key[0]);
+ *sw_if_index = key->key[1];
+}
+
+static void
+gbp_endpoint_extract_key_ip_itf (const clib_bihash_kv_24_8_t * key,
+ ip46_address_t * ip, u32 * sw_if_index)
+{
+ ip->as_u64[0] = key->key[0];
+ ip->as_u64[1] = key->key[1];
+ *sw_if_index = key->key[2];
+}
+
+gbp_endpoint_t *
+gbp_endpoint_find_ip (const ip46_address_t * ip, u32 fib_index)
+{
+ clib_bihash_kv_24_8_t key, value;
+ int rv;
+
+ gbp_endpoint_mk_key_ip (ip, fib_index, &key);
+
+ rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
+
+ if (0 != rv)
+ return NULL;
+
+ return (gbp_endpoint_get (value.value));
+}
+
+static void
+gbp_endpoint_add_itf (u32 sw_if_index, index_t gei)
+{
+ vec_validate_init_empty (gbp_ep_db.ged_by_sw_if_index, sw_if_index, ~0);
+
+ gbp_ep_db.ged_by_sw_if_index[sw_if_index] = gei;
+}
+
+static bool
+gbp_endpoint_add_mac (const mac_address_t * mac, u32 bd_index, index_t gei)
+{
+ clib_bihash_kv_16_8_t key;
+ int rv;
+
+ gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key);
+ key.value = gei;
+
+ rv = clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 1);
+
+
+ return (0 == rv);
+}
+
+static bool
+gbp_endpoint_add_ip (const ip46_address_t * ip, u32 fib_index, index_t gei)
+{
+ clib_bihash_kv_24_8_t key;
+ int rv;
+
+ gbp_endpoint_mk_key_ip (ip, fib_index, &key);
+ key.value = gei;
+
+ rv = clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 1);
+
+ return (0 == rv);
+}
+
+static void
+gbp_endpoint_del_mac (const mac_address_t * mac, u32 bd_index)
+{
+ clib_bihash_kv_16_8_t key;
+
+ gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key);
+
+ clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 0);
+}
+
+static void
+gbp_endpoint_del_ip (const ip46_address_t * ip, u32 fib_index)
+{
+ clib_bihash_kv_24_8_t key;
+
+ gbp_endpoint_mk_key_ip (ip, fib_index, &key);
+
+ clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 0);
+}
+
+static index_t
+gbp_endpoint_index (const gbp_endpoint_t * ge)
+{
+ return (ge - gbp_endpoint_pool);
+}
+
+static int
+gbp_endpoint_ip_is_equal (const fib_prefix_t * fp, const ip46_address_t * ip)
+{
+ return (ip46_address_is_equal (ip, &fp->fp_addr));
+}
+
+static void
+gbp_endpoint_ips_update (gbp_endpoint_t * ge,
+ const ip46_address_t * ips,
+ const gbp_route_domain_t * grd)
+{
+ const ip46_address_t *ip;
+ index_t gei, grdi;
+
+ gei = gbp_endpoint_index (ge);
+ grdi = gbp_route_domain_index (grd);
+
+ ASSERT ((ge->ge_key.gek_grd == INDEX_INVALID) ||
+ (ge->ge_key.gek_grd == grdi));
+
+ vec_foreach (ip, ips)
+ {
+ if (~0 == vec_search_with_function (ge->ge_key.gek_ips, ip,
+ gbp_endpoint_ip_is_equal))
+ {
+ fib_prefix_t *pfx;
+
+ vec_add2 (ge->ge_key.gek_ips, pfx, 1);
+ fib_prefix_from_ip46_addr (ip, pfx);
+
+ gbp_endpoint_add_ip (&pfx->fp_addr,
+ grd->grd_fib_index[pfx->fp_proto], gei);
+ }
+ ge->ge_key.gek_grd = grdi;
+ }
+}
+
+static gbp_endpoint_t *
+gbp_endpoint_alloc (const ip46_address_t * ips,
+ const gbp_route_domain_t * grd,
+ const mac_address_t * mac,
+ const gbp_bridge_domain_t * gbd)
+{
+ gbp_endpoint_t *ge;
+ index_t gei;
+
+ pool_get_zero (gbp_endpoint_pool, ge);
+
+ fib_node_init (&ge->ge_node, gbp_endpoint_fib_type);
+ gei = gbp_endpoint_index (ge);
+ ge->ge_key.gek_gbd =
+ ge->ge_key.gek_grd = ge->ge_fwd.gef_fib_index = INDEX_INVALID;
+ gbp_itf_hdl_reset (&ge->ge_fwd.gef_itf);
+ ge->ge_last_time = vlib_time_now (vlib_get_main ());
+ ge->ge_key.gek_gbd = gbp_bridge_domain_index (gbd);
+
+ if (NULL != mac)
+ {
+ mac_address_copy (&ge->ge_key.gek_mac, mac);
+ gbp_endpoint_add_mac (mac, gbd->gb_bd_index, gei);
+ }
+ gbp_endpoint_ips_update (ge, ips, grd);
+
+ return (ge);
+}
+
+static int
+gbp_endpoint_loc_is_equal (gbp_endpoint_loc_t * a, gbp_endpoint_loc_t * b)
+{
+ return (a->gel_src == b->gel_src);
+}
+
+static int
+gbp_endpoint_loc_cmp_for_sort (gbp_endpoint_loc_t * a, gbp_endpoint_loc_t * b)
+{
+ return (a->gel_src - b->gel_src);
+}
+
+static gbp_endpoint_loc_t *
+gbp_endpoint_loc_find (gbp_endpoint_t * ge, gbp_endpoint_src_t src)
+{
+ gbp_endpoint_loc_t gel = {
+ .gel_src = src,
+ };
+ u32 pos;
+
+ pos = vec_search_with_function (ge->ge_locs, &gel,
+ gbp_endpoint_loc_is_equal);
+
+ if (~0 != pos)
+ return (&ge->ge_locs[pos]);
+
+ return NULL;
+}
+
+static int
+gbp_endpoint_loc_unlock (gbp_endpoint_t * ge, gbp_endpoint_loc_t * gel)
+{
+ u32 pos;
+
+ gel->gel_locks--;
+
+ if (0 == gel->gel_locks)
+ {
+ pos = gel - ge->ge_locs;
+
+ vec_del1 (ge->ge_locs, pos);
+ if (vec_len (ge->ge_locs) > 1)
+ vec_sort_with_function (ge->ge_locs, gbp_endpoint_loc_cmp_for_sort);
+
+ /* This could be the last lock, so don't access the EP from
+ * this point on */
+ fib_node_unlock (&ge->ge_node);
+
+ return (1);
+ }
+ return (0);
+}
+
+static void
+gbp_endpoint_loc_destroy (gbp_endpoint_loc_t * gel)
+{
+ gbp_endpoint_group_unlock (gel->gel_epg);
+ gbp_itf_unlock (&gel->gel_itf);
+}
+
+static gbp_endpoint_loc_t *
+gbp_endpoint_loc_find_or_add (gbp_endpoint_t * ge, gbp_endpoint_src_t src)
+{
+ gbp_endpoint_loc_t gel = {
+ .gel_src = src,
+ .gel_epg = INDEX_INVALID,
+ .gel_itf = GBP_ITF_HDL_INVALID,
+ .gel_locks = 0,
+ };
+ u32 pos;
+
+ pos = vec_search_with_function (ge->ge_locs, &gel,
+ gbp_endpoint_loc_is_equal);
+
+ if (~0 == pos)
+ {
+ vec_add1 (ge->ge_locs, gel);
+
+ if (vec_len (ge->ge_locs) > 1)
+ {
+ vec_sort_with_function (ge->ge_locs, gbp_endpoint_loc_cmp_for_sort);
+
+ pos = vec_search_with_function (ge->ge_locs, &gel,
+ gbp_endpoint_loc_is_equal);
+ }
+ else
+ pos = 0;
+
+ /*
+ * it's the sources and children that lock the endpoints
+ */
+ fib_node_lock (&ge->ge_node);
+ }
+
+ return (&ge->ge_locs[pos]);
+}
+
+/**
+ * Find an EP inthe DBs and check that if we find it in the L2 DB
+ * it has the same IPs as this update
+ */
+static int
+gbp_endpoint_find_for_update (const ip46_address_t * ips,
+ const gbp_route_domain_t * grd,
+ const mac_address_t * mac,
+ const gbp_bridge_domain_t * gbd,
+ gbp_endpoint_t ** ge)
+{
+ gbp_endpoint_t *l2_ge, *l3_ge, *tmp;
+
+ l2_ge = l3_ge = NULL;
+
+ if (NULL != mac && !mac_address_is_zero (mac))
+ {
+ ASSERT (gbd);
+ l2_ge = gbp_endpoint_find_mac (mac->bytes, gbd->gb_bd_index);
+ }
+ if (NULL != ips && !ip46_address_is_zero (ips))
+ {
+ const ip46_address_t *ip;
+ fib_protocol_t fproto;
+
+ ASSERT (grd);
+ vec_foreach (ip, ips)
+ {
+ fproto = fib_proto_from_ip46 (ip46_address_get_type (ip));
+
+ tmp = gbp_endpoint_find_ip (ip, grd->grd_fib_index[fproto]);
+
+ if (NULL == tmp)
+ /* not found */
+ continue;
+ else if (NULL == l3_ge)
+ /* first match against an IP address */
+ l3_ge = tmp;
+ else if (tmp == l3_ge)
+ /* another match against IP address that is the same endpoint */
+ continue;
+ else
+ {
+ /*
+ * a match agains a different endpoint.
+ * this means the KEY of the EP is changing which is not allowed
+ */
+ return (-1);
+ }
+ }
+ }
+
+ if (NULL == l2_ge && NULL == l3_ge)
+ /* not found */
+ *ge = NULL;
+ else if (NULL == l2_ge)
+ /* found at L3 */
+ *ge = l3_ge;
+ else if (NULL == l3_ge)
+ /* found at L2 */
+ *ge = l2_ge;
+ else
+ {
+ /* found both L3 and L2 - they must be the same else the KEY
+ * is changing
+ */
+ if (l2_ge == l3_ge)
+ *ge = l2_ge;
+ else
+ return (-1);
+ }
+
+ return (0);
+}
+
+static gbp_endpoint_src_t
+gbp_endpoint_get_best_src (const gbp_endpoint_t * ge)
+{
+ if (0 == vec_len (ge->ge_locs))
+ return (GBP_ENDPOINT_SRC_MAX);
+
+ return (ge->ge_locs[0].gel_src);
+}
+
+static void
+gbp_endpoint_n_learned (int n)
+{
+ gbp_n_learnt_endpoints += n;
+
+ if (n > 0 && 1 == gbp_n_learnt_endpoints)
+ {
+ vlib_process_signal_event (vlib_get_main (),
+ gbp_scanner_node.index,
+ GBP_ENDPOINT_SCAN_START, 0);
+ }
+ if (n < 0 && 0 == gbp_n_learnt_endpoints)
+ {
+ vlib_process_signal_event (vlib_get_main (),
+ gbp_scanner_node.index,
+ GBP_ENDPOINT_SCAN_STOP, 0);
+ }
+}
+
+static void
+gbp_endpoint_loc_update (const gbp_endpoint_t * ge,
+ gbp_endpoint_loc_t * gel,
+ const gbp_bridge_domain_t * gb,
+ u32 sw_if_index,
+ index_t ggi,
+ gbp_endpoint_flags_t flags,
+ const ip46_address_t * tun_src,
+ const ip46_address_t * tun_dst)
+{
+ int was_learnt, is_learnt;
+
+ gel->gel_locks++;
+ was_learnt = ! !(gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE);
+ gel->gel_flags = flags;
+ is_learnt = ! !(gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE);
+
+ gbp_endpoint_n_learned (is_learnt - was_learnt);
+
+ /*
+ * update the EPG
+ */
+ gbp_endpoint_group_lock (ggi);
+ gbp_endpoint_group_unlock (gel->gel_epg);
+ gel->gel_epg = ggi;
+
+ if (gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE)
+ {
+ if (NULL != tun_src)
+ ip46_address_copy (&gel->tun.gel_src, tun_src);
+ if (NULL != tun_dst)
+ ip46_address_copy (&gel->tun.gel_dst, tun_dst);
+
+ if (ip46_address_is_multicast (&gel->tun.gel_src))
+ {
+ /*
+ * we learnt the EP from the multicast tunnel.
+ * Create a unicast TEP from the packet's source
+ * and the fixed address of the BD's parent tunnel
+ */
+ const gbp_vxlan_tunnel_t *gt;
+
+ gt = gbp_vxlan_tunnel_get (gb->gb_vni);
+
+ if (NULL != gt)
+ {
+ ip46_address_copy (&gel->tun.gel_src, &gt->gt_src);
+ sw_if_index = gt->gt_sw_if_index;
+ }
+ }
+
+ /*
+ * the input interface may be the parent GBP-vxlan interface,
+ * create a child vlxan-gbp tunnel and use that as the endpoint's
+ * interface.
+ */
+ gbp_itf_hdl_t old = gel->gel_itf;
+
+ switch (gbp_vxlan_tunnel_get_type (sw_if_index))
+ {
+ case GBP_VXLAN_TEMPLATE_TUNNEL:
+ gel->tun.gel_parent_sw_if_index = sw_if_index;
+ gel->gel_itf = gbp_vxlan_tunnel_clone_and_lock (sw_if_index,
+ &gel->tun.gel_src,
+ &gel->tun.gel_dst);
+ break;
+ case VXLAN_GBP_TUNNEL:
+ gel->tun.gel_parent_sw_if_index =
+ vxlan_gbp_tunnel_get_parent (sw_if_index);
+ gel->gel_itf = vxlan_gbp_tunnel_lock_itf (sw_if_index);
+ break;
+ }
+
+ gbp_itf_unlock (&old);
+ }
+ else
+ {
+ gel->gel_itf = gbp_itf_l2_add_and_lock (sw_if_index,
+ ge->ge_key.gek_gbd);
+ }
+}
+
+static void
+gbb_endpoint_fwd_reset (gbp_endpoint_t * ge)
+{
+ const gbp_route_domain_t *grd;
+ const gbp_bridge_domain_t *gbd;
+ gbp_endpoint_fwd_t *gef;
+ const fib_prefix_t *pfx;
+ index_t *ai;
+
+ gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd);
+ gef = &ge->ge_fwd;
+
+ vec_foreach (pfx, ge->ge_key.gek_ips)
+ {
+ u32 fib_index;
+
+ grd = gbp_route_domain_get (ge->ge_key.gek_grd);
+ fib_index = grd->grd_fib_index[pfx->fp_proto];
+
+ bd_add_del_ip_mac (gbd->gb_bd_index, fib_proto_to_ip46 (pfx->fp_proto),
+ &pfx->fp_addr, &ge->ge_key.gek_mac, 0);
+
+ /*
+ * remove a host route
+ */
+ if (gbp_endpoint_is_remote (ge))
+ {
+ fib_table_entry_special_remove (fib_index, pfx, gbp_fib_source_hi);
+ }
+
+ fib_table_entry_delete (fib_index, pfx, gbp_fib_source_low);
+ }
+ vec_foreach (ai, gef->gef_adjs)
+ {
+ adj_unlock (*ai);
+ }
+
+ if (gbp_itf_hdl_is_valid (gef->gef_itf))
+ {
+ l2fib_del_entry (ge->ge_key.gek_mac.bytes,
+ gbd->gb_bd_index,
+ gbp_itf_get_sw_if_index (gef->gef_itf));
+ }
+
+ gbp_itf_unlock (&gef->gef_itf);
+ vec_free (gef->gef_adjs);
+}
+
+static void
+gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
+{
+ const gbp_bridge_domain_t *gbd;
+ const gbp_endpoint_group_t *gg;
+ const gbp_route_domain_t *grd;
+ gbp_endpoint_loc_t *gel;
+ gbp_endpoint_fwd_t *gef;
+ const fib_prefix_t *pfx;
+ index_t gei;
+
+ /*
+ * locations are sort in source priority order
+ */
+ gei = gbp_endpoint_index (ge);
+ gel = &ge->ge_locs[0];
+ gef = &ge->ge_fwd;
+ gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd);
+
+ gef->gef_flags = gel->gel_flags;
+
+ if (INDEX_INVALID != gel->gel_epg)
+ {
+ gg = gbp_endpoint_group_get (gel->gel_epg);
+ gef->gef_sclass = gg->gg_sclass;
+ }
+ else
+ {
+ gg = NULL;
+ }
+
+ gef->gef_itf = gbp_itf_clone_and_lock (gel->gel_itf);
+
+ if (!mac_address_is_zero (&ge->ge_key.gek_mac))
+ {
+ gbp_itf_l2_set_input_feature (gef->gef_itf, L2INPUT_FEAT_GBP_FWD);
+
+ if (gbp_endpoint_is_remote (ge) || gbp_endpoint_is_external (ge))
+ {
+ /*
+ * bridged packets to external endpoints should be classifed
+ * based on the EP's/BD's EPG
+ */
+ gbp_itf_l2_set_output_feature (gef->gef_itf,
+ L2OUTPUT_FEAT_GBP_POLICY_MAC);
+ }
+ else
+ {
+ gbp_endpoint_add_itf (gbp_itf_get_sw_if_index (gef->gef_itf), gei);
+ gbp_itf_l2_set_output_feature (gef->gef_itf,
+ L2OUTPUT_FEAT_GBP_POLICY_PORT);
+ }
+ l2fib_add_entry (ge->ge_key.gek_mac.bytes,
+ gbd->gb_bd_index,
+ gbp_itf_get_sw_if_index (gef->gef_itf),
+ L2FIB_ENTRY_RESULT_FLAG_STATIC);
+ }
+
+ vec_foreach (pfx, ge->ge_key.gek_ips)
+ {
+ ethernet_header_t *eth;
+ u32 ip_sw_if_index;
+ u32 fib_index;
+ u8 *rewrite;
+ index_t ai;
+
+ rewrite = NULL;
+ grd = gbp_route_domain_get (ge->ge_key.gek_grd);
+ fib_index = grd->grd_fib_index[pfx->fp_proto];
+ gef->gef_fib_index = fib_index;
+
+ bd_add_del_ip_mac (gbd->gb_bd_index, fib_proto_to_ip46 (pfx->fp_proto),
+ &pfx->fp_addr, &ge->ge_key.gek_mac, 1);
+
+ /*
+ * add a host route via the EPG's BVI we need this because the
+ * adj fib does not install, due to cover refinement check, since
+ * the BVI's prefix is /32
+ */
+ vec_validate (rewrite, sizeof (*eth) - 1);
+ eth = (ethernet_header_t *) rewrite;
+
+ eth->type = clib_host_to_net_u16 ((pfx->fp_proto == FIB_PROTOCOL_IP4 ?
+ ETHERNET_TYPE_IP4 :
+ ETHERNET_TYPE_IP6));
+
+ if (gbp_endpoint_is_remote (ge))
+ {
+ /*
+ * for dynamic EPs we must add the IP adjacency via the learned
+ * tunnel since the BD will not contain the EP's MAC since it was
+ * L3 learned. The dst MAC address used is the 'BD's MAC'.
+ */
+ ip_sw_if_index = gbp_itf_get_sw_if_index (gef->gef_itf);
+
+ mac_address_to_bytes (gbp_route_domain_get_local_mac (),
+ eth->src_address);
+ mac_address_to_bytes (gbp_route_domain_get_remote_mac (),
+ eth->dst_address);
+ }
+ else
+ {
+ /*
+ * for the static EPs we add the IP adjacency via the BVI
+ * knowing that the BD has the MAC address to route to and
+ * that policy will be applied on egress to the EP's port
+ */
+ ip_sw_if_index = gbd->gb_bvi_sw_if_index;
+
+ clib_memcpy (eth->src_address,
+ vnet_sw_interface_get_hw_address (vnet_get_main (),
+ ip_sw_if_index),
+ sizeof (eth->src_address));
+ mac_address_to_bytes (&ge->ge_key.gek_mac, eth->dst_address);
+ }
+
+ fib_table_entry_path_add (fib_index, pfx,
+ gbp_fib_source_low,
+ FIB_ENTRY_FLAG_NONE,
+ fib_proto_to_dpo (pfx->fp_proto),
+ &pfx->fp_addr, ip_sw_if_index,
+ ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
+
+ ai = adj_nbr_add_or_lock_w_rewrite (pfx->fp_proto,
+ fib_proto_to_link (pfx->fp_proto),
+ &pfx->fp_addr,
+ ip_sw_if_index, rewrite);
+ vec_add1 (gef->gef_adjs, ai);
+
+ /*
+ * if the endpoint is external then routed packet to it must be
+ * classifed to the BD's EPG. but this will happen anyway with
+ * the GBP_MAC classification.
+ */
+
+ if (NULL != gg)
+ {
+ if (gbp_endpoint_is_remote (ge))
+ {
+ dpo_id_t policy_dpo = DPO_INVALID;
+
+ /*
+ * interpose a policy DPO from the endpoint so that policy
+ * is applied
+ */
+ gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto),
+ grd->grd_scope,
+ gg->gg_sclass, ~0, &policy_dpo);
+
+ fib_table_entry_special_dpo_add (fib_index, pfx,
+ gbp_fib_source_hi,
+ FIB_ENTRY_FLAG_INTERPOSE,
+ &policy_dpo);
+ dpo_reset (&policy_dpo);
+ }
+
+ /*
+ * send a gratuitous ARP on the EPG's uplink. this is done so
+ * that if this EP has moved from some other place in the
+ * 'fabric', upstream devices are informed
+ */
+ if (gbp_endpoint_is_local (ge) && ~0 != gg->gg_uplink_sw_if_index)
+ {
+ gbp_endpoint_add_itf (gbp_itf_get_sw_if_index (gef->gef_itf),
+ gei);
+ if (FIB_PROTOCOL_IP4 == pfx->fp_proto)
+ ip4_neighbor_advertise (
+ vlib_get_main (), vnet_get_main (), gg->gg_uplink_sw_if_index,
+ vlib_get_thread_index (), &pfx->fp_addr.ip4);
+ else
+ ip6_neighbor_advertise (
+ vlib_get_main (), vnet_get_main (), gg->gg_uplink_sw_if_index,
+ vlib_get_thread_index (), &pfx->fp_addr.ip6);
+ }
+ }
+ }
+
+ if (gbp_endpoint_is_external (ge))
+ {
+ gbp_itf_l2_set_input_feature (gef->gef_itf,
+ L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+ }
+ else if (gbp_endpoint_is_local (ge))
+ {
+ /*
+ * non-remote endpoints (i.e. those not arriving on iVXLAN
+ * tunnels) need to be classifed based on the the input interface.
+ * We enable the GBP-FWD feature only if the group has an uplink
+ * interface (on which the GBP-FWD feature would send UU traffic).
+ * External endpoints get classified based on an LPM match
+ */
+ l2input_feat_masks_t feats = L2INPUT_FEAT_GBP_SRC_CLASSIFY;
+
+ if (NULL != gg && ~0 != gg->gg_uplink_sw_if_index)
+ feats |= L2INPUT_FEAT_GBP_FWD;
+ gbp_itf_l2_set_input_feature (gef->gef_itf, feats);
+ }
+
+ /*
+ * update children with the new forwarding info
+ */
+ fib_node_back_walk_ctx_t bw_ctx = {
+ .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
+ .fnbw_flags = FIB_NODE_BW_FLAG_FORCE_SYNC,
+ };
+
+ fib_walk_sync (gbp_endpoint_fib_type, gei, &bw_ctx);
+}
+
+int
+gbp_endpoint_update_and_lock (gbp_endpoint_src_t src,
+ u32 sw_if_index,
+ const ip46_address_t * ips,
+ const mac_address_t * mac,
+ index_t gbdi, index_t grdi,
+ sclass_t sclass,
+ gbp_endpoint_flags_t flags,
+ const ip46_address_t * tun_src,
+ const ip46_address_t * tun_dst, u32 * handle)
+{
+ gbp_bridge_domain_t *gbd;
+ gbp_endpoint_group_t *gg;
+ gbp_endpoint_src_t best;
+ gbp_route_domain_t *grd;
+ gbp_endpoint_loc_t *gel;
+ gbp_endpoint_t *ge;
+ index_t ggi, gei;
+ int rv;
+
+ if (~0 == sw_if_index)
+ return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
+
+ ge = NULL;
+ gg = NULL;
+
+ /*
+ * we need to determine the bridge-domain, either from the EPG or
+ * the BD passed
+ */
+ if (SCLASS_INVALID != sclass)
+ {
+ ggi = gbp_endpoint_group_find (sclass);
+
+ if (INDEX_INVALID == ggi)
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+ gg = gbp_endpoint_group_get (ggi);
+ gbdi = gg->gg_gbd;
+ grdi = gg->gg_rd;
+ }
+ else
+ {
+ if (INDEX_INVALID == gbdi)
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+ if (INDEX_INVALID == grdi)
+ return (VNET_API_ERROR_NO_SUCH_FIB);
+ ggi = INDEX_INVALID;
+ }
+
+ gbd = gbp_bridge_domain_get (gbdi);
+ grd = gbp_route_domain_get (grdi);
+ rv = gbp_endpoint_find_for_update (ips, grd, mac, gbd, &ge);
+
+ if (0 != rv)
+ return (rv);
+
+ if (NULL == ge)
+ {
+ ge = gbp_endpoint_alloc (ips, grd, mac, gbd);
+ }
+ else
+ {
+ gbp_endpoint_ips_update (ge, ips, grd);
+ }
+
+ best = gbp_endpoint_get_best_src (ge);
+ gei = gbp_endpoint_index (ge);
+ gel = gbp_endpoint_loc_find_or_add (ge, src);
+
+ gbp_endpoint_loc_update (ge, gel, gbd, sw_if_index, ggi, flags,
+ tun_src, tun_dst);
+
+ if (src <= best)
+ {
+ /*
+ * either the best source has been updated or we have a new best source
+ */
+ gbb_endpoint_fwd_reset (ge);
+ gbb_endpoint_fwd_recalc (ge);
+ }
+ else
+ {
+ /*
+ * an update to a lower priority source, so we need do nothing
+ */
+ }
+
+ if (handle)
+ *handle = gei;
+
+ GBP_ENDPOINT_INFO ("update: %U", format_gbp_endpoint, gei);
+
+ return (0);
+}
+
+void
+gbp_endpoint_unlock (gbp_endpoint_src_t src, index_t gei)
+{
+ gbp_endpoint_loc_t *gel, gel_copy;
+ gbp_endpoint_src_t best;
+ gbp_endpoint_t *ge;
+ int removed;
+
+ if (pool_is_free_index (gbp_endpoint_pool, gei))
+ return;
+
+ GBP_ENDPOINT_INFO ("delete: %U", format_gbp_endpoint, gei);
+
+ ge = gbp_endpoint_get (gei);
+
+ gel = gbp_endpoint_loc_find (ge, src);
+
+ if (NULL == gel)
+ return;
+
+ /*
+ * lock the EP so we can control when it is deleted
+ */
+ fib_node_lock (&ge->ge_node);
+ best = gbp_endpoint_get_best_src (ge);
+
+ /*
+ * copy the location info since we'll lose it when it's removed from
+ * the vector
+ */
+ clib_memcpy (&gel_copy, gel, sizeof (gel_copy));
+
+ /*
+ * remove the source we no longer need
+ */
+ removed = gbp_endpoint_loc_unlock (ge, gel);
+
+ if (src == best)
+ {
+ /*
+ * we have removed the old best source => recalculate fwding
+ */
+ if (0 == vec_len (ge->ge_locs))
+ {
+ /*
+ * if there are no more sources left, then we need only release
+ * the fwding resources held and then this EP is gawn.
+ */
+ gbb_endpoint_fwd_reset (ge);
+ }
+ else
+ {
+ /*
+ * else there are more sources. release the old and get new
+ * fwding objects
+ */
+ gbb_endpoint_fwd_reset (ge);
+ gbb_endpoint_fwd_recalc (ge);
+ }
+ }
+ /*
+ * else
+ * we removed a lower priority source so we need to do nothing
+ */
+
+ /*
+ * clear up any resources held by the source
+ */
+ if (removed)
+ gbp_endpoint_loc_destroy (&gel_copy);
+
+ /*
+ * remove the lock taken above
+ */
+ fib_node_unlock (&ge->ge_node);
+ /*
+ * We may have removed the last source and so this EP is now TOAST
+ * DO NOTHING BELOW HERE
+ */
+}
+
+u32
+gbp_endpoint_child_add (index_t gei,
+ fib_node_type_t type, fib_node_index_t index)
+{
+ return (fib_node_child_add (gbp_endpoint_fib_type, gei, type, index));
+}
+
+void
+gbp_endpoint_child_remove (index_t gei, u32 sibling)
+{
+ return (fib_node_child_remove (gbp_endpoint_fib_type, gei, sibling));
+}
+
+typedef struct gbp_endpoint_flush_ctx_t_
+{
+ u32 sw_if_index;
+ gbp_endpoint_src_t src;
+ index_t *geis;
+} gbp_endpoint_flush_ctx_t;
+
+static walk_rc_t
+gbp_endpoint_flush_cb (index_t gei, void *args)
+{
+ gbp_endpoint_flush_ctx_t *ctx = args;
+ gbp_endpoint_loc_t *gel;
+ gbp_endpoint_t *ge;
+
+ ge = gbp_endpoint_get (gei);
+ gel = gbp_endpoint_loc_find (ge, ctx->src);
+
+ if ((NULL != gel) && ctx->sw_if_index == gel->tun.gel_parent_sw_if_index)
+ {
+ vec_add1 (ctx->geis, gei);
+ }
+
+ return (WALK_CONTINUE);
+}
+
+/**
+ * remove all learnt endpoints using the interface
+ */
+void
+gbp_endpoint_flush (gbp_endpoint_src_t src, u32 sw_if_index)
+{
+ gbp_endpoint_flush_ctx_t ctx = {
+ .sw_if_index = sw_if_index,
+ .src = src,
+ };
+ index_t *gei;
+
+ GBP_ENDPOINT_INFO ("flush: %U %U",
+ format_gbp_endpoint_src, src,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index);
+ gbp_endpoint_walk (gbp_endpoint_flush_cb, &ctx);
+
+ vec_foreach (gei, ctx.geis)
+ {
+ gbp_endpoint_unlock (src, *gei);
+ }
+
+ vec_free (ctx.geis);
+}
+
+void
+gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
+{
+ u32 index;
+
+ /* *INDENT-OFF* */
+ pool_foreach_index (index, gbp_endpoint_pool)
+ {
+ if (!cb(index, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_endpoint_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ ip46_address_t ip = ip46_address_initializer, *ips = NULL;
+ mac_address_t mac = ZERO_MAC_ADDRESS;
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sclass = SCLASS_INVALID;
+ u32 handle = INDEX_INVALID;
+ u32 sw_if_index = ~0;
+ u32 flags = GBP_ENDPOINT_FLAG_NONE;
+ u8 add = 1;
+ int rv;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ ip46_address_reset (&ip);
+
+ if (unformat (input, "%U", unformat_vnet_sw_interface,
+ vnm, &sw_if_index))
+ ;
+ else if (unformat (input, "add"))
+ add = 1;
+ else if (unformat (input, "del"))
+ add = 0;
+ else if (unformat (input, "sclass %d", &sclass))
+ ;
+ else if (unformat (input, "handle %d", &handle))
+ ;
+ else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
+ vec_add1 (ips, ip);
+ else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
+ vec_add1 (ips, ip);
+ else if (unformat (input, "mac %U", unformat_mac_address, &mac))
+ ;
+ else if (unformat (input, "flags 0x%x", &flags))
+ ;
+ else
+ break;
+ }
+
+ if (add)
+ {
+ if (~0 == sw_if_index)
+ return clib_error_return (0, "interface must be specified");
+ if (SCLASS_INVALID == sclass)
+ return clib_error_return (0, "SCLASS must be specified");
+
+ rv =
+ gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP,
+ sw_if_index, ips, &mac,
+ INDEX_INVALID, INDEX_INVALID,
+ sclass, flags, NULL, NULL, &handle);
+
+ if (rv)
+ return clib_error_return (0, "GBP Endpoint update returned %d", rv);
+ else
+ vlib_cli_output (vm, "handle %d\n", handle);
+ }
+ else
+ {
+ if (INDEX_INVALID == handle)
+ return clib_error_return (0, "handle must be specified");
+
+ gbp_endpoint_unlock (GBP_ENDPOINT_SRC_CP, handle);
+ }
+
+ vec_free (ips);
+
+ return (NULL);
+}
+
+/*?
+ * Configure a GBP Endpoint
+ *
+ * @cliexpar
+ * @cliexstart{gbp endpoint del <handle> | [add] <interface> sclass <SCLASS> ip <IP> mac <MAC> [flags <flags>]}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
+ .path = "gbp endpoint",
+ .short_help = "gbp endpoint del <handle> | [add] <interface> sclass <SCLASS> ip <IP> mac <MAC> [flags <flags>]",
+ .function = gbp_endpoint_cli,
+};
+/* *INDENT-ON* */
+
+u8 *
+format_gbp_endpoint_src (u8 * s, va_list * args)
+{
+ gbp_endpoint_src_t action = va_arg (*args, gbp_endpoint_src_t);
+
+ switch (action)
+ {
+#define _(v,a) case GBP_ENDPOINT_SRC_##v: return (format (s, "%s", a));
+ foreach_gbp_endpoint_src
+#undef _
+ }
+
+ return (format (s, "unknown"));
+}
+
+static u8 *
+format_gbp_endpoint_fwd (u8 * s, va_list * args)
+{
+ gbp_endpoint_fwd_t *gef = va_arg (*args, gbp_endpoint_fwd_t *);
+
+ s = format (s, "fwd:");
+ s = format (s, "\n itf:[%U]", format_gbp_itf_hdl, gef->gef_itf);
+ if (GBP_ENDPOINT_FLAG_NONE != gef->gef_flags)
+ {
+ s = format (s, " flags:%U", format_gbp_endpoint_flags, gef->gef_flags);
+ }
+
+ return (s);
+}
+
+static u8 *
+format_gbp_endpoint_key (u8 * s, va_list * args)
+{
+ gbp_endpoint_key_t *gek = va_arg (*args, gbp_endpoint_key_t *);
+ const fib_prefix_t *pfx;
+
+ s = format (s, "ips:[");
+
+ vec_foreach (pfx, gek->gek_ips)
+ {
+ s = format (s, "%U, ", format_fib_prefix, pfx);
+ }
+ s = format (s, "]");
+
+ s = format (s, " mac:%U", format_mac_address_t, &gek->gek_mac);
+
+ return (s);
+}
+
+static u8 *
+format_gbp_endpoint_loc (u8 * s, va_list * args)
+{
+ gbp_endpoint_loc_t *gel = va_arg (*args, gbp_endpoint_loc_t *);
+
+ s = format (s, "%U", format_gbp_endpoint_src, gel->gel_src);
+ s = format (s, "\n EPG:%d [%U]", gel->gel_epg,
+ format_gbp_itf_hdl, gel->gel_itf);
+
+ if (GBP_ENDPOINT_FLAG_NONE != gel->gel_flags)
+ {
+ s = format (s, " flags:%U", format_gbp_endpoint_flags, gel->gel_flags);
+ }
+ if (GBP_ENDPOINT_FLAG_REMOTE & gel->gel_flags)
+ {
+ s = format (s, " tun:[");
+ s = format (s, "parent:%U", format_vnet_sw_if_index_name,
+ vnet_get_main (), gel->tun.gel_parent_sw_if_index);
+ s = format (s, " {%U,%U}]",
+ format_ip46_address, &gel->tun.gel_src, IP46_TYPE_ANY,
+ format_ip46_address, &gel->tun.gel_dst, IP46_TYPE_ANY);
+ }
+
+ return (s);
+}
+
+u8 *
+format_gbp_endpoint (u8 * s, va_list * args)
+{
+ index_t gei = va_arg (*args, index_t);
+ gbp_endpoint_loc_t *gel;
+ gbp_endpoint_t *ge;
+
+ ge = gbp_endpoint_get (gei);
+
+ s = format (s, "[@%d] %U", gei, format_gbp_endpoint_key, &ge->ge_key);
+ s = format (s, " last-time:[%f]", ge->ge_last_time);
+
+ vec_foreach (gel, ge->ge_locs)
+ {
+ s = format (s, "\n %U", format_gbp_endpoint_loc, gel);
+ }
+ s = format (s, "\n %U", format_gbp_endpoint_fwd, &ge->ge_fwd);
+
+ return s;
+}
+
+static walk_rc_t
+gbp_endpoint_show_one (index_t gei, void *ctx)
+{
+ vlib_main_t *vm;
+
+ vm = ctx;
+ vlib_cli_output (vm, " %U", format_gbp_endpoint, gei);
+
+ return (WALK_CONTINUE);
+}
+
+static int
+gbp_endpoint_walk_ip_itf (clib_bihash_kv_24_8_t * kvp, void *arg)
+{
+ ip46_address_t ip;
+ vlib_main_t *vm;
+ u32 sw_if_index;
+
+ vm = arg;
+
+ gbp_endpoint_extract_key_ip_itf (kvp, &ip, &sw_if_index);
+
+ vlib_cli_output (vm, " {%U, %U} -> %d",
+ format_ip46_address, &ip, IP46_TYPE_ANY,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, kvp->value);
+ return (BIHASH_WALK_CONTINUE);
+}
+
+static int
+gbp_endpoint_walk_mac_itf (clib_bihash_kv_16_8_t * kvp, void *arg)
+{
+ mac_address_t mac;
+ vlib_main_t *vm;
+ u32 sw_if_index;
+
+ vm = arg;
+
+ gbp_endpoint_extract_key_mac_itf (kvp, &mac, &sw_if_index);
+
+ vlib_cli_output (vm, " {%U, %U} -> %d",
+ format_mac_address_t, &mac,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, kvp->value);
+ return (BIHASH_WALK_CONTINUE);
+}
+
+static clib_error_t *
+gbp_endpoint_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ u32 show_dbs, handle;
+
+ handle = INDEX_INVALID;
+ show_dbs = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%d", &handle))
+ ;
+ else if (unformat (input, "db"))
+ show_dbs = 1;
+ else
+ break;
+ }
+
+ if (INDEX_INVALID != handle)
+ {
+ vlib_cli_output (vm, "%U", format_gbp_endpoint, handle);
+ }
+ else if (show_dbs)
+ {
+ vlib_cli_output (vm, "\nDatabases:");
+ clib_bihash_foreach_key_value_pair_24_8 (&gbp_ep_db.ged_by_ip_rd,
+ gbp_endpoint_walk_ip_itf, vm);
+ clib_bihash_foreach_key_value_pair_16_8
+ (&gbp_ep_db.ged_by_mac_bd, gbp_endpoint_walk_mac_itf, vm);
+ }
+ else
+ {
+ vlib_cli_output (vm, "Endpoints:");
+ gbp_endpoint_walk (gbp_endpoint_show_one, vm);
+ }
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Endpoints and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp endpoint}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
+ .path = "show gbp endpoint",
+ .short_help = "show gbp endpoint\n",
+ .function = gbp_endpoint_show,
+};
+/* *INDENT-ON* */
+
+static void
+gbp_endpoint_check (index_t gei, f64 start_time)
+{
+ gbp_endpoint_group_t *gg;
+ gbp_endpoint_loc_t *gel;
+ gbp_endpoint_t *ge;
+
+ ge = gbp_endpoint_get (gei);
+ gel = gbp_endpoint_loc_find (ge, GBP_ENDPOINT_SRC_DP);
+
+ if (NULL != gel)
+ {
+ gg = gbp_endpoint_group_get (gel->gel_epg);
+
+ if ((start_time - ge->ge_last_time) >
+ gg->gg_retention.remote_ep_timeout)
+ {
+ gbp_endpoint_unlock (GBP_ENDPOINT_SRC_DP, gei);
+ }
+ }
+}
+
+static void
+gbp_endpoint_scan_l2 (vlib_main_t * vm)
+{
+ clib_bihash_16_8_t *gte_table = &gbp_ep_db.ged_by_mac_bd;
+ f64 last_start, start_time, delta_t;
+ int i, j, k;
+
+ if (!gte_table->instantiated)
+ return;
+
+ delta_t = 0;
+ last_start = start_time = vlib_time_now (vm);
+
+ for (i = 0; i < gte_table->nbuckets; i++)
+ {
+ clib_bihash_bucket_16_8_t *b;
+ clib_bihash_value_16_8_t *v;
+
+ /* allow no more than 20us without a pause */
+ delta_t = vlib_time_now (vm) - last_start;
+ if (delta_t > 20e-6)
+ {
+ /* suspend for 100 us */
+ vlib_process_suspend (vm, 100e-6);
+ last_start = vlib_time_now (vm);
+ }
+
+ b = clib_bihash_get_bucket_16_8 (gte_table, i);
+ if (clib_bihash_bucket_is_empty_16_8 (b))
+ continue;
+ v = clib_bihash_get_value_16_8 (gte_table, b->offset);
+
+ for (j = 0; j < (1 << b->log2_pages); j++)
+ {
+ for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
+ {
+ if (clib_bihash_is_free_16_8 (&v->kvp[k]))
+ continue;
+
+ gbp_endpoint_check (v->kvp[k].value, start_time);
+
+ /*
+ * Note: we may have just freed the bucket's backing
+ * storage, so check right here...
+ */
+ if (clib_bihash_bucket_is_empty_16_8 (b))
+ goto doublebreak;
+ }
+ v++;
+ }
+ doublebreak:
+ ;
+ }
+}
+
+static void
+gbp_endpoint_scan_l3 (vlib_main_t * vm)
+{
+ clib_bihash_24_8_t *gte_table = &gbp_ep_db.ged_by_ip_rd;
+ f64 last_start, start_time, delta_t;
+ int i, j, k;
+
+ if (!gte_table->instantiated)
+ return;
+
+ delta_t = 0;
+ last_start = start_time = vlib_time_now (vm);
+
+ for (i = 0; i < gte_table->nbuckets; i++)
+ {
+ clib_bihash_bucket_24_8_t *b;
+ clib_bihash_value_24_8_t *v;
+
+ /* allow no more than 20us without a pause */
+ delta_t = vlib_time_now (vm) - last_start;
+ if (delta_t > 20e-6)
+ {
+ /* suspend for 100 us */
+ vlib_process_suspend (vm, 100e-6);
+ last_start = vlib_time_now (vm);
+ }
+
+ b = clib_bihash_get_bucket_24_8 (gte_table, i);
+ if (clib_bihash_bucket_is_empty_24_8 (b))
+ continue;
+ v = clib_bihash_get_value_24_8 (gte_table, b->offset);
+
+ for (j = 0; j < (1 << b->log2_pages); j++)
+ {
+ for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
+ {
+ if (clib_bihash_is_free_24_8 (&v->kvp[k]))
+ continue;
+
+ gbp_endpoint_check (v->kvp[k].value, start_time);
+
+ /*
+ * Note: we may have just freed the bucket's backing
+ * storage, so check right here...
+ */
+ if (clib_bihash_bucket_is_empty_24_8 (b))
+ goto doublebreak;
+ }
+ v++;
+ }
+ doublebreak:
+ ;
+ }
+}
+
+void
+gbp_endpoint_scan (vlib_main_t * vm)
+{
+ gbp_endpoint_scan_l2 (vm);
+ gbp_endpoint_scan_l3 (vm);
+}
+
+static fib_node_t *
+gbp_endpoint_get_node (fib_node_index_t index)
+{
+ gbp_endpoint_t *ge;
+
+ ge = gbp_endpoint_get (index);
+
+ return (&ge->ge_node);
+}
+
+static gbp_endpoint_t *
+gbp_endpoint_from_fib_node (fib_node_t * node)
+{
+ ASSERT (gbp_endpoint_fib_type == node->fn_type);
+ return ((gbp_endpoint_t *) node);
+}
+
+static void
+gbp_endpoint_last_lock_gone (fib_node_t * node)
+{
+ const gbp_bridge_domain_t *gbd;
+ const gbp_route_domain_t *grd;
+ const fib_prefix_t *pfx;
+ gbp_endpoint_t *ge;
+
+ ge = gbp_endpoint_from_fib_node (node);
+
+ ASSERT (0 == vec_len (ge->ge_locs));
+
+ gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd);
+
+ /*
+ * we have removed the last source. this EP is toast
+ */
+ if (INDEX_INVALID != ge->ge_key.gek_gbd)
+ {
+ gbp_endpoint_del_mac (&ge->ge_key.gek_mac, gbd->gb_bd_index);
+ }
+ vec_foreach (pfx, ge->ge_key.gek_ips)
+ {
+ grd = gbp_route_domain_get (ge->ge_key.gek_grd);
+ gbp_endpoint_del_ip (&pfx->fp_addr, grd->grd_fib_index[pfx->fp_proto]);
+ }
+ pool_put (gbp_endpoint_pool, ge);
+}
+
+static fib_node_back_walk_rc_t
+gbp_endpoint_back_walk_notify (fib_node_t * node,
+ fib_node_back_walk_ctx_t * ctx)
+{
+ ASSERT (0);
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/*
+ * The FIB path's graph node virtual function table
+ */
+static const fib_node_vft_t gbp_endpoint_vft = {
+ .fnv_get = gbp_endpoint_get_node,
+ .fnv_last_lock = gbp_endpoint_last_lock_gone,
+ .fnv_back_walk = gbp_endpoint_back_walk_notify,
+ // .fnv_mem_show = fib_path_memory_show,
+};
+
+static clib_error_t *
+gbp_endpoint_init (vlib_main_t * vm)
+{
+#define GBP_EP_HASH_NUM_BUCKETS (2 * 1024)
+#define GBP_EP_HASH_MEMORY_SIZE (1 << 20)
+
+ clib_bihash_init_24_8 (&gbp_ep_db.ged_by_ip_rd,
+ "GBP Endpoints - IP/RD",
+ GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
+
+ clib_bihash_init_16_8 (&gbp_ep_db.ged_by_mac_bd,
+ "GBP Endpoints - MAC/BD",
+ GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
+
+ gbp_ep_logger = vlib_log_register_class ("gbp", "ep");
+ gbp_endpoint_fib_type = fib_node_register_new_type (&gbp_endpoint_vft);
+ gbp_fib_source_hi = fib_source_allocate ("gbp-endpoint-hi",
+ FIB_SOURCE_PRIORITY_HI,
+ FIB_SOURCE_BH_SIMPLE);
+ gbp_fib_source_low = fib_source_allocate ("gbp-endpoint-low",
+ FIB_SOURCE_PRIORITY_LOW,
+ FIB_SOURCE_BH_SIMPLE);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_endpoint_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint.h b/extras/deprecated/plugins/gbp/gbp_endpoint.h
new file mode 100644
index 00000000000..3155e7be4e0
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_endpoint.h
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_ENDPOINT_H__
+#define __GBP_ENDPOINT_H__
+
+#include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/mac_address.h>
+
+#include <vppinfra/bihash_16_8.h>
+#include <vppinfra/bihash_template.h>
+#include <vppinfra/bihash_24_8.h>
+#include <vppinfra/bihash_template.h>
+
+/**
+ * Flags for each endpoint
+ */
+typedef enum gbp_endpoint_attr_t_
+{
+ GBP_ENDPOINT_ATTR_FIRST = 0,
+ GBP_ENDPOINT_ATTR_BOUNCE = GBP_ENDPOINT_ATTR_FIRST,
+ GBP_ENDPOINT_ATTR_REMOTE,
+ GBP_ENDPOINT_ATTR_LEARNT,
+ GBP_ENDPOINT_ATTR_EXTERNAL,
+ GBP_ENDPOINT_ATTR_LAST,
+} gbp_endpoint_attr_t;
+
+typedef enum gbp_endpoint_flags_t_
+{
+ GBP_ENDPOINT_FLAG_NONE = 0,
+ GBP_ENDPOINT_FLAG_BOUNCE = (1 << GBP_ENDPOINT_ATTR_BOUNCE),
+ GBP_ENDPOINT_FLAG_REMOTE = (1 << GBP_ENDPOINT_ATTR_REMOTE),
+ GBP_ENDPOINT_FLAG_LEARNT = (1 << GBP_ENDPOINT_ATTR_LEARNT),
+ GBP_ENDPOINT_FLAG_EXTERNAL = (1 << GBP_ENDPOINT_ATTR_EXTERNAL),
+} gbp_endpoint_flags_t;
+
+#define GBP_ENDPOINT_ATTR_NAMES { \
+ [GBP_ENDPOINT_ATTR_BOUNCE] = "bounce", \
+ [GBP_ENDPOINT_ATTR_REMOTE] = "remote", \
+ [GBP_ENDPOINT_ATTR_LEARNT] = "learnt", \
+ [GBP_ENDPOINT_ATTR_EXTERNAL] = "external", \
+}
+
+extern u8 *format_gbp_endpoint_flags (u8 * s, va_list * args);
+
+/**
+ * Sources of Endpoints in priority order. The best (lowest value) source
+ * provides the forwarding information.
+ * Data-plane takes preference because the CP data is not always complete,
+ * it may not have the sclass.
+ */
+#define foreach_gbp_endpoint_src \
+ _(DP, "data-plane") \
+ _(CP, "control-plane") \
+ _(RR, "recursive-resolution")
+
+typedef enum gbp_endpoint_src_t_
+{
+#define _(v,s) GBP_ENDPOINT_SRC_##v,
+ foreach_gbp_endpoint_src
+#undef _
+} gbp_endpoint_src_t;
+
+#define GBP_ENDPOINT_SRC_MAX (GBP_ENDPOINT_SRC_RR+1)
+
+extern u8 *format_gbp_endpoint_src (u8 * s, va_list * args);
+
+/**
+ * This is the identity of an endpoint, as such it is information
+ * about an endpoint that is idempotent.
+ * The ID is used to add the EP into the various data-bases for retrieval.
+ */
+typedef struct gbp_endpoint_key_t_
+{
+ /**
+ * A vector of ip addresses that belong to the endpoint.
+ * Together with the route EPG's RD this forms the EP's L3 key
+ */
+ fib_prefix_t *gek_ips;
+
+ /**
+ * MAC address of the endpoint.
+ * Together with the route EPG's BD this forms the EP's L2 key
+ */
+ mac_address_t gek_mac;
+
+ /**
+ * Index of the Bridge-Domain
+ */
+ index_t gek_gbd;
+
+ /**
+ * Index of the Route-Domain
+ */
+ index_t gek_grd;
+} gbp_endpoint_key_t;
+
+/**
+ * Information about the location of the endpoint provided by a source
+ * of endpoints
+ */
+typedef struct gbp_endpoint_loc_t_
+{
+ /**
+ * The source providing this location information
+ */
+ gbp_endpoint_src_t gel_src;
+
+ /**
+ * The interface on which the EP is connected
+ */
+ gbp_itf_hdl_t gel_itf;
+
+ /**
+ * Endpoint flags
+ */
+ gbp_endpoint_flags_t gel_flags;
+
+ /**
+ * Endpoint Group.
+ */
+ index_t gel_epg;
+
+ /**
+ * number of times this source has locked this
+ */
+ u32 gel_locks;
+
+ /**
+ * Tunnel info for remote endpoints
+ */
+ struct
+ {
+ u32 gel_parent_sw_if_index;
+ ip46_address_t gel_src;
+ ip46_address_t gel_dst;
+ } tun;
+} gbp_endpoint_loc_t;
+
+/**
+ * And endpoints current forwarding state
+ */
+typedef struct gbp_endpoint_fwd_t_
+{
+ /**
+ * The interface on which the EP is connected
+ */
+ gbp_itf_hdl_t gef_itf;
+
+ /**
+ * The L3 adj, if created
+ */
+ index_t *gef_adjs;
+
+ /**
+ * Endpoint Group's sclass. cached for fast DP access.
+ */
+ sclass_t gef_sclass;
+
+ /**
+ * FIB index the EP is in
+ */
+ u32 gef_fib_index;
+
+ gbp_endpoint_flags_t gef_flags;
+} gbp_endpoint_fwd_t;
+
+/**
+ * A Group Based Policy Endpoint.
+ * This is typically a VM or container. If the endpoint is local (i.e. on
+ * the same compute node as VPP) then there is one interface per-endpoint.
+ * If the EP is remote,e.g. reachable over a [vxlan] tunnel, then there
+ * will be multiple EPs reachable over the tunnel and they can be distinguished
+ * via either their MAC or IP Address[es].
+ */
+typedef struct gbp_endpoint_t_
+{
+ /**
+ * A FIB node that allows the tracking of children.
+ */
+ fib_node_t ge_node;
+
+ /**
+ * The key/ID of this EP
+ */
+ gbp_endpoint_key_t ge_key;
+
+ /**
+ * Location information provided by the various sources.
+ * These are sorted based on source priority.
+ */
+ gbp_endpoint_loc_t *ge_locs;
+
+ gbp_endpoint_fwd_t ge_fwd;
+
+ /**
+ * The last time a packet from seen from this end point
+ */
+ f64 ge_last_time;
+} gbp_endpoint_t;
+
+extern u8 *format_gbp_endpoint (u8 * s, va_list * args);
+
+/**
+ * GBP Endpoint Databases
+ */
+typedef struct gbp_ep_by_ip_itf_db_t_
+{
+ index_t *ged_by_sw_if_index;
+ clib_bihash_24_8_t ged_by_ip_rd;
+ clib_bihash_16_8_t ged_by_mac_bd;
+} gbp_ep_db_t;
+
+extern int gbp_endpoint_update_and_lock (gbp_endpoint_src_t src,
+ u32 sw_if_index,
+ const ip46_address_t * ip,
+ const mac_address_t * mac,
+ index_t gbd, index_t grd,
+ sclass_t sclass,
+ gbp_endpoint_flags_t flags,
+ const ip46_address_t * tun_src,
+ const ip46_address_t * tun_dst,
+ u32 * handle);
+extern void gbp_endpoint_unlock (gbp_endpoint_src_t src, index_t gbpei);
+extern u32 gbp_endpoint_child_add (index_t gei,
+ fib_node_type_t type,
+ fib_node_index_t index);
+extern void gbp_endpoint_child_remove (index_t gei, u32 sibling);
+
+typedef walk_rc_t (*gbp_endpoint_cb_t) (index_t gbpei, void *ctx);
+extern void gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx);
+extern void gbp_endpoint_scan (vlib_main_t * vm);
+extern int gbp_endpoint_is_remote (const gbp_endpoint_t * ge);
+extern int gbp_endpoint_is_local (const gbp_endpoint_t * ge);
+extern int gbp_endpoint_is_external (const gbp_endpoint_t * ge);
+extern int gbp_endpoint_is_learnt (const gbp_endpoint_t * ge);
+
+
+extern void gbp_endpoint_flush (gbp_endpoint_src_t src, u32 sw_if_index);
+
+/**
+ * DP functions and databases
+ */
+extern gbp_ep_db_t gbp_ep_db;
+extern gbp_endpoint_t *gbp_endpoint_pool;
+
+/**
+ * Get the endpoint from a port/interface
+ */
+always_inline gbp_endpoint_t *
+gbp_endpoint_get (index_t gbpei)
+{
+ return (pool_elt_at_index (gbp_endpoint_pool, gbpei));
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_mac (const u8 * mac,
+ u32 bd_index, clib_bihash_kv_16_8_t * key)
+{
+ key->key[0] = ethernet_mac_address_u64 (mac);
+ key->key[1] = bd_index;
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_mac (const u8 * mac, u32 bd_index)
+{
+ clib_bihash_kv_16_8_t key, value;
+ int rv;
+
+ gbp_endpoint_mk_key_mac (mac, bd_index, &key);
+
+ rv = clib_bihash_search_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, &value);
+
+ if (0 != rv)
+ return NULL;
+
+ return (gbp_endpoint_get (value.value));
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_ip (const ip46_address_t * ip,
+ u32 fib_index, clib_bihash_kv_24_8_t * key)
+{
+ key->key[0] = ip->as_u64[0];
+ key->key[1] = ip->as_u64[1];
+ key->key[2] = fib_index;
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_ip4 (const ip4_address_t * ip,
+ u32 fib_index, clib_bihash_kv_24_8_t * key)
+{
+ const ip46_address_t a = {
+ .ip4 = *ip,
+ };
+ gbp_endpoint_mk_key_ip (&a, fib_index, key);
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_ip4 (const ip4_address_t * ip, u32 fib_index)
+{
+ clib_bihash_kv_24_8_t key, value;
+ int rv;
+
+ gbp_endpoint_mk_key_ip4 (ip, fib_index, &key);
+
+ rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
+
+ if (0 != rv)
+ return NULL;
+
+ return (gbp_endpoint_get (value.value));
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_ip6 (const ip6_address_t * ip,
+ u32 fib_index, clib_bihash_kv_24_8_t * key)
+{
+ key->key[0] = ip->as_u64[0];
+ key->key[1] = ip->as_u64[1];
+ key->key[2] = fib_index;
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_ip6 (const ip6_address_t * ip, u32 fib_index)
+{
+ clib_bihash_kv_24_8_t key, value;
+ int rv;
+
+ gbp_endpoint_mk_key_ip6 (ip, fib_index, &key);
+
+ rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
+
+ if (0 != rv)
+ return NULL;
+
+ return (gbp_endpoint_get (value.value));
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_itf (u32 sw_if_index)
+{
+ index_t gei;
+
+ gei = gbp_ep_db.ged_by_sw_if_index[sw_if_index];
+
+ if (INDEX_INVALID != gei)
+ return (gbp_endpoint_get (gei));
+
+ return (NULL);
+}
+
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint_group.c b/extras/deprecated/plugins/gbp/gbp_endpoint_group.c
new file mode 100644
index 00000000000..b9044378e3b
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_endpoint_group.c
@@ -0,0 +1,402 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_itf.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/l2/l2_input.h>
+
+/**
+ * Pool of GBP endpoint_groups
+ */
+gbp_endpoint_group_t *gbp_endpoint_group_pool;
+
+/**
+ * DB of endpoint_groups
+ */
+gbp_endpoint_group_db_t gbp_endpoint_group_db;
+
+/**
+ * Map sclass to EPG
+ */
+uword *gbp_epg_sclass_db;
+
+vlib_log_class_t gg_logger;
+
+#define GBP_EPG_DBG(...) \
+ vlib_log_debug (gg_logger, __VA_ARGS__);
+
+gbp_endpoint_group_t *
+gbp_endpoint_group_get (index_t i)
+{
+ return (pool_elt_at_index (gbp_endpoint_group_pool, i));
+}
+
+void
+gbp_endpoint_group_lock (index_t ggi)
+{
+ gbp_endpoint_group_t *gg;
+
+ if (INDEX_INVALID == ggi)
+ return;
+
+ gg = gbp_endpoint_group_get (ggi);
+ gg->gg_locks++;
+}
+
+index_t
+gbp_endpoint_group_find (sclass_t sclass)
+{
+ uword *p;
+
+ p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass);
+
+ if (NULL != p)
+ return p[0];
+
+ return (INDEX_INVALID);
+}
+
+int
+gbp_endpoint_group_add_and_lock (vnid_t vnid,
+ u16 sclass,
+ u32 bd_id,
+ u32 rd_id,
+ u32 uplink_sw_if_index,
+ const gbp_endpoint_retention_t * retention)
+{
+ gbp_endpoint_group_t *gg;
+ index_t ggi;
+
+ ggi = gbp_endpoint_group_find (sclass);
+
+ if (INDEX_INVALID == ggi)
+ {
+ fib_protocol_t fproto;
+ index_t gbi, grdi;
+
+ gbi = gbp_bridge_domain_find_and_lock (bd_id);
+
+ if (~0 == gbi)
+ return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
+
+ grdi = gbp_route_domain_find_and_lock (rd_id);
+
+ if (~0 == grdi)
+ {
+ gbp_bridge_domain_unlock (gbi);
+ return (VNET_API_ERROR_NO_SUCH_FIB);
+ }
+
+ pool_get_zero (gbp_endpoint_group_pool, gg);
+
+ gg->gg_vnid = vnid;
+ gg->gg_rd = grdi;
+ gg->gg_gbd = gbi;
+
+ gg->gg_uplink_sw_if_index = uplink_sw_if_index;
+ gbp_itf_hdl_reset (&gg->gg_uplink_itf);
+ gg->gg_locks = 1;
+ gg->gg_sclass = sclass;
+ gg->gg_retention = *retention;
+
+ if (SCLASS_INVALID != gg->gg_sclass)
+ hash_set (gbp_epg_sclass_db, gg->gg_sclass, gg->gg_vnid);
+
+ /*
+ * an egress DVR dpo for internal subnets to use when sending
+ * on the uplink interface
+ */
+ if (~0 != gg->gg_uplink_sw_if_index)
+ {
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ dvr_dpo_add_or_lock (uplink_sw_if_index,
+ fib_proto_to_dpo (fproto),
+ &gg->gg_dpo[fproto]);
+ }
+
+ /*
+ * Add the uplink to the BD
+ * packets direct from the uplink have had policy applied
+ */
+ gg->gg_uplink_itf =
+ gbp_itf_l2_add_and_lock (gg->gg_uplink_sw_if_index, gbi);
+
+ gbp_itf_l2_set_input_feature (gg->gg_uplink_itf,
+ L2INPUT_FEAT_GBP_NULL_CLASSIFY);
+ }
+
+ hash_set (gbp_endpoint_group_db.gg_hash_sclass,
+ gg->gg_sclass, gg - gbp_endpoint_group_pool);
+ }
+ else
+ {
+ gg = gbp_endpoint_group_get (ggi);
+ gg->gg_locks++;
+ }
+
+ GBP_EPG_DBG ("add: %U", format_gbp_endpoint_group, gg);
+
+ return (0);
+}
+
+void
+gbp_endpoint_group_unlock (index_t ggi)
+{
+ gbp_endpoint_group_t *gg;
+
+ if (INDEX_INVALID == ggi)
+ return;
+
+ gg = gbp_endpoint_group_get (ggi);
+
+ gg->gg_locks--;
+
+ if (0 == gg->gg_locks)
+ {
+ fib_protocol_t fproto;
+
+ gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi);
+
+ gbp_itf_unlock (&gg->gg_uplink_itf);
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ dpo_reset (&gg->gg_dpo[fproto]);
+ }
+ gbp_bridge_domain_unlock (gg->gg_gbd);
+ gbp_route_domain_unlock (gg->gg_rd);
+
+ if (SCLASS_INVALID != gg->gg_sclass)
+ hash_unset (gbp_epg_sclass_db, gg->gg_sclass);
+ hash_unset (gbp_endpoint_group_db.gg_hash_sclass, gg->gg_sclass);
+
+ pool_put (gbp_endpoint_group_pool, gg);
+ }
+}
+
+int
+gbp_endpoint_group_delete (sclass_t sclass)
+{
+ index_t ggi;
+
+ ggi = gbp_endpoint_group_find (sclass);
+
+ if (INDEX_INVALID != ggi)
+ {
+ GBP_EPG_DBG ("del: %U", format_gbp_endpoint_group,
+ gbp_endpoint_group_get (ggi));
+ gbp_endpoint_group_unlock (ggi);
+
+ return (0);
+ }
+
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+u32
+gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t * gg)
+{
+ const gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (gg->gg_gbd);
+
+ return (gb->gb_bd_id);
+}
+
+index_t
+gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t * gg,
+ fib_protocol_t fproto)
+{
+ const gbp_route_domain_t *grd;
+
+ grd = gbp_route_domain_get (gg->gg_rd);
+
+ return (grd->grd_fib_index[fproto]);
+}
+
+void
+gbp_endpoint_group_walk (gbp_endpoint_group_cb_t cb, void *ctx)
+{
+ gbp_endpoint_group_t *gbpe;
+
+ /* *INDENT-OFF* */
+ pool_foreach (gbpe, gbp_endpoint_group_pool)
+ {
+ if (!cb(gbpe, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_endpoint_group_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ gbp_endpoint_retention_t retention = { 0 };
+ vnid_t vnid = VNID_INVALID, sclass;
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 uplink_sw_if_index = ~0;
+ u32 bd_id = ~0;
+ u32 rd_id = ~0;
+ u8 add = 1;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U", unformat_vnet_sw_interface,
+ vnm, &uplink_sw_if_index))
+ ;
+ else if (unformat (input, "add"))
+ add = 1;
+ else if (unformat (input, "del"))
+ add = 0;
+ else if (unformat (input, "epg %d", &vnid))
+ ;
+ else if (unformat (input, "sclass %d", &sclass))
+ ;
+ else if (unformat (input, "bd %d", &bd_id))
+ ;
+ else if (unformat (input, "rd %d", &rd_id))
+ ;
+ else
+ break;
+ }
+
+ if (VNID_INVALID == vnid)
+ return clib_error_return (0, "EPG-ID must be specified");
+
+ if (add)
+ {
+ if (~0 == bd_id)
+ return clib_error_return (0, "Bridge-domain must be specified");
+ if (~0 == rd_id)
+ return clib_error_return (0, "route-domain must be specified");
+
+ gbp_endpoint_group_add_and_lock (vnid, sclass, bd_id, rd_id,
+ uplink_sw_if_index, &retention);
+ }
+ else
+ gbp_endpoint_group_delete (vnid);
+
+ return (NULL);
+}
+
+/*?
+ * Configure a GBP Endpoint Group
+ *
+ * @cliexpar
+ * @cliexstart{gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> [sclass <ID>] [<interface>]}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = {
+ .path = "gbp endpoint-group",
+ .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> [sclass <ID>] [<interface>]",
+ .function = gbp_endpoint_group_cli,
+};
+
+static u8 *
+format_gbp_endpoint_retention (u8 * s, va_list * args)
+{
+ gbp_endpoint_retention_t *rt = va_arg (*args, gbp_endpoint_retention_t*);
+
+ s = format (s, "[remote-EP-timeout:%d]", rt->remote_ep_timeout);
+
+ return (s);
+}
+
+u8 *
+format_gbp_endpoint_group (u8 * s, va_list * args)
+{
+ gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*);
+
+ if (NULL != gg)
+ s = format (s, "[%d] %d, sclass:%d bd:%d rd:%d uplink:%U retention:%U locks:%d",
+ gg - gbp_endpoint_group_pool,
+ gg->gg_vnid,
+ gg->gg_sclass,
+ gg->gg_gbd,
+ gg->gg_rd,
+ format_gbp_itf_hdl, gg->gg_uplink_itf,
+ format_gbp_endpoint_retention, &gg->gg_retention,
+ gg->gg_locks);
+ else
+ s = format (s, "NULL");
+
+ return (s);
+}
+
+static int
+gbp_endpoint_group_show_one (gbp_endpoint_group_t *gg, void *ctx)
+{
+ vlib_main_t *vm;
+
+ vm = ctx;
+ vlib_cli_output (vm, " %U",format_gbp_endpoint_group, gg);
+
+ return (1);
+}
+
+static clib_error_t *
+gbp_endpoint_group_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vlib_cli_output (vm, "Endpoint-Groups:");
+ gbp_endpoint_group_walk (gbp_endpoint_group_show_one, vm);
+
+ return (NULL);
+}
+
+
+/*?
+ * Show Group Based Policy Endpoint_Groups and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp endpoint_group}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = {
+ .path = "show gbp endpoint-group",
+ .short_help = "show gbp endpoint-group\n",
+ .function = gbp_endpoint_group_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_endpoint_group_init (vlib_main_t * vm)
+{
+ gg_logger = vlib_log_register_class ("gbp", "epg");
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_endpoint_group_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint_group.h b/extras/deprecated/plugins/gbp/gbp_endpoint_group.h
new file mode 100644
index 00000000000..c5fdff8463d
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_endpoint_group.h
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_ENDPOINT_GROUP_H__
+#define __GBP_ENDPOINT_GROUP_H__
+
+#include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
+
+#include <vnet/fib/fib_types.h>
+
+/**
+ * Endpoint Retnetion Policy
+ */
+typedef struct gbp_endpoint_retention_t_
+{
+ /** Aging timeout for remote endpoints */
+ u32 remote_ep_timeout;
+} gbp_endpoint_retention_t;
+
+/**
+ * An Endpoint Group representation
+ */
+typedef struct gpb_endpoint_group_t_
+{
+ /**
+ * ID
+ */
+ vnid_t gg_vnid;
+
+ /**
+ * Sclass. Could be unset => ~0
+ */
+ u16 gg_sclass;
+
+ /**
+ * Bridge-domain ID the EPG is in
+ */
+ index_t gg_gbd;
+
+ /**
+ * route-domain/IP-table ID the EPG is in
+ */
+ index_t gg_rd;
+
+ /**
+ * Is the EPG an external/NAT
+ */
+ u8 gg_is_ext;
+
+ /**
+ * the uplink interface dedicated to the EPG
+ */
+ u32 gg_uplink_sw_if_index;
+ gbp_itf_hdl_t gg_uplink_itf;
+
+ /**
+ * The DPO used in the L3 path for forwarding internal subnets
+ */
+ dpo_id_t gg_dpo[FIB_PROTOCOL_IP_MAX];
+
+ /**
+ * Locks/references to this EPG
+ */
+ u32 gg_locks;
+
+ /**
+ * EP retention policy
+ */
+ gbp_endpoint_retention_t gg_retention;
+} gbp_endpoint_group_t;
+
+/**
+ * EPG DB, key'd on EGP-ID
+ */
+typedef struct gbp_endpoint_group_db_t_
+{
+ uword *gg_hash_sclass;
+} gbp_endpoint_group_db_t;
+
+extern int gbp_endpoint_group_add_and_lock (vnid_t vnid,
+ u16 sclass,
+ u32 bd_id,
+ u32 rd_id,
+ u32 uplink_sw_if_index,
+ const gbp_endpoint_retention_t *
+ retention);
+extern index_t gbp_endpoint_group_find (sclass_t sclass);
+extern int gbp_endpoint_group_delete (sclass_t sclass);
+extern void gbp_endpoint_group_unlock (index_t index);
+extern void gbp_endpoint_group_lock (index_t index);
+extern u32 gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t *);
+
+extern gbp_endpoint_group_t *gbp_endpoint_group_get (index_t i);
+extern index_t gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t *
+ gg, fib_protocol_t fproto);
+
+typedef int (*gbp_endpoint_group_cb_t) (gbp_endpoint_group_t * gbpe,
+ void *ctx);
+extern void gbp_endpoint_group_walk (gbp_endpoint_group_cb_t bgpe, void *ctx);
+
+
+extern u8 *format_gbp_endpoint_group (u8 * s, va_list * args);
+
+/**
+ * DP functions and databases
+ */
+extern gbp_endpoint_group_db_t gbp_endpoint_group_db;
+extern gbp_endpoint_group_t *gbp_endpoint_group_pool;
+extern uword *gbp_epg_sclass_db;
+
+always_inline u32
+gbp_epg_itf_lookup_sclass (sclass_t sclass)
+{
+ uword *p;
+
+ p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass);
+
+ if (NULL != p)
+ {
+ gbp_endpoint_group_t *gg;
+
+ gg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+ return (gg->gg_uplink_sw_if_index);
+ }
+ return (~0);
+}
+
+always_inline const dpo_id_t *
+gbp_epg_dpo_lookup (sclass_t sclass, fib_protocol_t fproto)
+{
+ uword *p;
+
+ p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass);
+
+ if (NULL != p)
+ {
+ gbp_endpoint_group_t *gg;
+
+ gg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+ return (&gg->gg_dpo[fproto]);
+ }
+ return (NULL);
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_ext_itf.c b/extras/deprecated/plugins/gbp/gbp_ext_itf.c
new file mode 100644
index 00000000000..c5506661c2d
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_ext_itf.c
@@ -0,0 +1,293 @@
+/*
+ * 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 <plugins/gbp/gbp_ext_itf.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_itf.h>
+
+/**
+ * Pool of GBP ext_itfs
+ */
+gbp_ext_itf_t *gbp_ext_itf_pool;
+
+/**
+ * external interface configs keyed by sw_if_index
+ */
+index_t *gbp_ext_itf_db;
+
+#define GBP_EXT_ITF_ID 0x00000080
+
+/**
+ * logger
+ */
+vlib_log_class_t gx_logger;
+
+#define GBP_EXT_ITF_DBG(...) \
+ vlib_log_debug (gx_logger, __VA_ARGS__);
+
+u8 *
+format_gbp_ext_itf (u8 * s, va_list * args)
+{
+ gbp_ext_itf_t *gx = va_arg (*args, gbp_ext_itf_t *);
+
+ return (format (s, "%U%s in %U",
+ format_gbp_itf_hdl, gx->gx_itf,
+ (gx->gx_flags & GBP_EXT_ITF_F_ANON) ? " [anon]" : "",
+ format_gbp_bridge_domain, gx->gx_bd));
+}
+
+int
+gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id, u32 flags)
+{
+ gbp_ext_itf_t *gx;
+ index_t gxi;
+
+ vec_validate_init_empty (gbp_ext_itf_db, sw_if_index, INDEX_INVALID);
+
+ gxi = gbp_ext_itf_db[sw_if_index];
+
+ if (INDEX_INVALID == gxi)
+ {
+ gbp_route_domain_t *gr;
+ fib_protocol_t fproto;
+ index_t gbi, gri;
+
+ gbi = gbp_bridge_domain_find_and_lock (bd_id);
+
+ if (INDEX_INVALID == gbi)
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+ gri = gbp_route_domain_find_and_lock (rd_id);
+
+ if (INDEX_INVALID == gri)
+ {
+ gbp_bridge_domain_unlock (gbi);
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+ }
+
+ pool_get_zero (gbp_ext_itf_pool, gx);
+ gxi = gx - gbp_ext_itf_pool;
+
+ gr = gbp_route_domain_get (gri);
+
+ gx->gx_bd = gbi;
+ gx->gx_rd = gri;
+ gbp_itf_hdl_reset (&gx->gx_itf);
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ gx->gx_fib_index[fproto] =
+ gr->grd_fib_index[fib_proto_to_dpo (fproto)];
+ }
+
+ if (flags & GBP_EXT_ITF_F_ANON)
+ {
+ /* add interface to the BD */
+ gx->gx_itf = gbp_itf_l2_add_and_lock (sw_if_index, gbi);
+
+ /* setup GBP L2 features on this interface */
+ gbp_itf_l2_set_input_feature (gx->gx_itf,
+ L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY |
+ L2INPUT_FEAT_LEARN);
+ gbp_itf_l2_set_output_feature (gx->gx_itf,
+ L2OUTPUT_FEAT_GBP_POLICY_LPM);
+ }
+
+ gx->gx_flags = flags;
+
+ gbp_ext_itf_db[sw_if_index] = gxi;
+
+ GBP_EXT_ITF_DBG ("add: %U", format_gbp_ext_itf, gx);
+
+ return (0);
+ }
+
+ return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
+}
+
+int
+gbp_ext_itf_delete (u32 sw_if_index)
+{
+ gbp_ext_itf_t *gx;
+ index_t gxi;
+
+ if (vec_len (gbp_ext_itf_db) <= sw_if_index)
+ return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
+
+ gxi = gbp_ext_itf_db[sw_if_index];
+
+ if (INDEX_INVALID != gxi)
+ {
+ gx = pool_elt_at_index (gbp_ext_itf_pool, gxi);
+
+ GBP_EXT_ITF_DBG ("del: %U", format_gbp_ext_itf, gx);
+
+ gbp_itf_unlock (&gx->gx_itf);
+ gbp_route_domain_unlock (gx->gx_rd);
+ gbp_bridge_domain_unlock (gx->gx_bd);
+
+ gbp_ext_itf_db[sw_if_index] = INDEX_INVALID;
+ pool_put (gbp_ext_itf_pool, gx);
+
+ return (0);
+ }
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+static clib_error_t *
+gbp_ext_itf_add_del_cli (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, bd_id = ~0, rd_id = ~0, flags = 0;
+ int add = 1;
+ int rv;
+
+ /* 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, "del"))
+ add = 0;
+ else
+ if (unformat
+ (line_input, "%U", unformat_vnet_sw_interface, vnet_get_main (),
+ &sw_if_index))
+ ;
+ else if (unformat (line_input, "bd %d", &bd_id))
+ ;
+ else if (unformat (line_input, "rd %d", &rd_id))
+ ;
+ else if (unformat (line_input, "anon-l3-out"))
+ flags |= GBP_EXT_ITF_F_ANON;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ }
+ unformat_free (line_input);
+
+ if (~0 == sw_if_index)
+ return clib_error_return (0, "interface must be specified");
+
+ if (add)
+ {
+ if (~0 == bd_id)
+ return clib_error_return (0, "BD-ID must be specified");
+ if (~0 == rd_id)
+ return clib_error_return (0, "RD-ID must be specified");
+ rv = gbp_ext_itf_add (sw_if_index, bd_id, rd_id, flags);
+ }
+ else
+ rv = gbp_ext_itf_delete (sw_if_index);
+
+ switch (rv)
+ {
+ case 0:
+ return 0;
+ case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
+ return clib_error_return (0, "interface already exists");
+ case VNET_API_ERROR_NO_SUCH_ENTRY: /* fallthrough */
+ case VNET_API_ERROR_INVALID_SW_IF_INDEX:
+ return clib_error_return (0, "unknown interface");
+ default:
+ return clib_error_return (0, "error %d", rv);
+ }
+
+ /* never reached */
+ return 0;
+}
+
+/*?
+ * Add Group Based Interface as anonymous L3out interface
+ *
+ * @cliexpar
+ * @cliexstart{gbp interface [del] anon-l3out <interface> bd <ID>}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_itf_anon_l3out_add_del_node, static) = {
+ .path = "gbp ext-itf",
+ .short_help = "gbp ext-itf [del] <interface> bd <ID> rd <ID> [anon-l3-out]\n",
+ .function = gbp_ext_itf_add_del_cli,
+};
+/* *INDENT-ON* */
+
+void
+gbp_ext_itf_walk (gbp_ext_itf_cb_t cb, void *ctx)
+{
+ gbp_ext_itf_t *ge;
+
+ /* *INDENT-OFF* */
+ pool_foreach (ge, gbp_ext_itf_pool)
+ {
+ if (!cb(ge, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static walk_rc_t
+gbp_ext_itf_show_one (gbp_ext_itf_t * gx, void *ctx)
+{
+ vlib_cli_output (ctx, " %U", format_gbp_ext_itf, gx);
+
+ return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+gbp_ext_itf_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vlib_cli_output (vm, "External-Interfaces:");
+ gbp_ext_itf_walk (gbp_ext_itf_show_one, vm);
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy external interface and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp ext-itf}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_ext_itf_show_node, static) = {
+ .path = "show gbp ext-itf",
+ .short_help = "show gbp ext-itf\n",
+ .function = gbp_ext_itf_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_ext_itf_init (vlib_main_t * vm)
+{
+ gx_logger = vlib_log_register_class ("gbp", "ext-itf");
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_ext_itf_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_ext_itf.h b/extras/deprecated/plugins/gbp/gbp_ext_itf.h
new file mode 100644
index 00000000000..03b1992ca45
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_ext_itf.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_EXT_ITF_H__
+#define __GBP_EXT_ITF_H__
+
+#include <gbp/gbp.h>
+
+enum
+{
+ GBP_EXT_ITF_F_NONE = 0,
+ GBP_EXT_ITF_F_ANON = 1 << 0,
+};
+
+/**
+ * An external interface maps directly to an oflex L3ExternalInterface.
+ * The special characteristics of an external interface is the way the source
+ * EPG is determined for input packets which, like a recirc interface, is via
+ * a LPM.
+ */
+typedef struct gpb_ext_itf_t_
+{
+ /**
+ * The interface
+ */
+ gbp_itf_hdl_t gx_itf;
+
+ /**
+ * The BD this external interface is a member of
+ */
+ index_t gx_bd;
+
+ /**
+ * The RD this external interface is a member of
+ */
+ index_t gx_rd;
+
+ /**
+ * cached FIB indices from the RD
+ */
+ u32 gx_fib_index[DPO_PROTO_NUM];
+
+ /**
+ * The associated flags
+ */
+ u32 gx_flags;
+
+} gbp_ext_itf_t;
+
+
+extern int gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id, u32 flags);
+extern int gbp_ext_itf_delete (u32 sw_if_index);
+
+extern u8 *format_gbp_ext_itf (u8 * s, va_list * args);
+
+typedef walk_rc_t (*gbp_ext_itf_cb_t) (gbp_ext_itf_t * gbpe, void *ctx);
+extern void gbp_ext_itf_walk (gbp_ext_itf_cb_t bgpe, void *ctx);
+
+
+/**
+ * Exposed types for the data-plane
+ */
+extern gbp_ext_itf_t *gbp_ext_itf_pool;
+extern index_t *gbp_ext_itf_db;
+
+always_inline gbp_ext_itf_t *
+gbp_ext_itf_get (u32 sw_if_index)
+{
+ return (pool_elt_at_index (gbp_ext_itf_pool, gbp_ext_itf_db[sw_if_index]));
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_fwd.c b/extras/deprecated/plugins/gbp/gbp_fwd.c
new file mode 100644
index 00000000000..4ecc4779b92
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_fwd.c
@@ -0,0 +1,56 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <vnet/l2/l2_input.h>
+#include <plugins/gbp/gbp_learn.h>
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_fwd_main_t_
+{
+ /**
+ * Next nodes for L2 output features
+ */
+ u32 l2_input_feat_next[32];
+} gbp_fwd_main_t;
+
+gbp_fwd_main_t gbp_fwd_main;
+
+static clib_error_t *
+gbp_fwd_init (vlib_main_t * vm)
+{
+ gbp_fwd_main_t *gpm = &gbp_fwd_main;
+ vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-fwd");
+
+ /* Initialize the feature next-node indices */
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ gpm->l2_input_feat_next);
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_fwd_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_fwd_dpo.c b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.c
new file mode 100644
index 00000000000..b1023f5e78f
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.c
@@ -0,0 +1,306 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_fwd_dpo.h>
+
+#include <vnet/ethernet/ethernet.h>
+
+
+#ifndef CLIB_MARCH_VARIANT
+/**
+ * The 'DB' of GBP FWD DPOs.
+ * There is one per-proto
+ */
+static index_t gbp_fwd_dpo_db[DPO_PROTO_NUM] = { INDEX_INVALID };
+
+/**
+ * DPO type registered for these GBP FWD
+ */
+static dpo_type_t gbp_fwd_dpo_type;
+
+/**
+ * @brief pool of all interface DPOs
+ */
+gbp_fwd_dpo_t *gbp_fwd_dpo_pool;
+
+static gbp_fwd_dpo_t *
+gbp_fwd_dpo_alloc (void)
+{
+ gbp_fwd_dpo_t *gfd;
+
+ pool_get (gbp_fwd_dpo_pool, gfd);
+
+ return (gfd);
+}
+
+static inline gbp_fwd_dpo_t *
+gbp_fwd_dpo_get_from_dpo (const dpo_id_t * dpo)
+{
+ ASSERT (gbp_fwd_dpo_type == dpo->dpoi_type);
+
+ return (gbp_fwd_dpo_get (dpo->dpoi_index));
+}
+
+static inline index_t
+gbp_fwd_dpo_get_index (gbp_fwd_dpo_t * gfd)
+{
+ return (gfd - gbp_fwd_dpo_pool);
+}
+
+static void
+gbp_fwd_dpo_lock (dpo_id_t * dpo)
+{
+ gbp_fwd_dpo_t *gfd;
+
+ gfd = gbp_fwd_dpo_get_from_dpo (dpo);
+ gfd->gfd_locks++;
+}
+
+static void
+gbp_fwd_dpo_unlock (dpo_id_t * dpo)
+{
+ gbp_fwd_dpo_t *gfd;
+
+ gfd = gbp_fwd_dpo_get_from_dpo (dpo);
+ gfd->gfd_locks--;
+
+ if (0 == gfd->gfd_locks)
+ {
+ gbp_fwd_dpo_db[gfd->gfd_proto] = INDEX_INVALID;
+ pool_put (gbp_fwd_dpo_pool, gfd);
+ }
+}
+
+void
+gbp_fwd_dpo_add_or_lock (dpo_proto_t dproto, dpo_id_t * dpo)
+{
+ gbp_fwd_dpo_t *gfd;
+
+ if (INDEX_INVALID == gbp_fwd_dpo_db[dproto])
+ {
+ gfd = gbp_fwd_dpo_alloc ();
+
+ gfd->gfd_proto = dproto;
+
+ gbp_fwd_dpo_db[dproto] = gbp_fwd_dpo_get_index (gfd);
+ }
+ else
+ {
+ gfd = gbp_fwd_dpo_get (gbp_fwd_dpo_db[dproto]);
+ }
+
+ dpo_set (dpo, gbp_fwd_dpo_type, dproto, gbp_fwd_dpo_get_index (gfd));
+}
+
+u8 *
+format_gbp_fwd_dpo (u8 * s, va_list * ap)
+{
+ index_t index = va_arg (*ap, index_t);
+ CLIB_UNUSED (u32 indent) = va_arg (*ap, u32);
+ gbp_fwd_dpo_t *gfd = gbp_fwd_dpo_get (index);
+
+ return (format (s, "gbp-fwd-dpo: %U", format_dpo_proto, gfd->gfd_proto));
+}
+
+const static dpo_vft_t gbp_fwd_dpo_vft = {
+ .dv_lock = gbp_fwd_dpo_lock,
+ .dv_unlock = gbp_fwd_dpo_unlock,
+ .dv_format = format_gbp_fwd_dpo,
+};
+
+/**
+ * @brief The per-protocol VLIB graph nodes that are assigned to a glean
+ * object.
+ *
+ * this means that these graph nodes are ones from which a glean is the
+ * parent object in the DPO-graph.
+ */
+const static char *const gbp_fwd_dpo_ip4_nodes[] = {
+ "ip4-gbp-fwd-dpo",
+ NULL,
+};
+
+const static char *const gbp_fwd_dpo_ip6_nodes[] = {
+ "ip6-gbp-fwd-dpo",
+ NULL,
+};
+
+const static char *const *const gbp_fwd_dpo_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = gbp_fwd_dpo_ip4_nodes,
+ [DPO_PROTO_IP6] = gbp_fwd_dpo_ip6_nodes,
+};
+
+dpo_type_t
+gbp_fwd_dpo_get_type (void)
+{
+ return (gbp_fwd_dpo_type);
+}
+
+static clib_error_t *
+gbp_fwd_dpo_module_init (vlib_main_t * vm)
+{
+ dpo_proto_t dproto;
+
+ FOR_EACH_DPO_PROTO (dproto)
+ {
+ gbp_fwd_dpo_db[dproto] = INDEX_INVALID;
+ }
+
+ gbp_fwd_dpo_type = dpo_register_new_type (&gbp_fwd_dpo_vft,
+ gbp_fwd_dpo_nodes);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_fwd_dpo_module_init);
+#endif /* CLIB_MARCH_VARIANT */
+
+typedef struct gbp_fwd_dpo_trace_t_
+{
+ u32 sclass;
+ u32 dpo_index;
+} gbp_fwd_dpo_trace_t;
+
+typedef enum
+{
+ GBP_FWD_DROP,
+ GBP_FWD_FWD,
+ GBP_FWD_N_NEXT,
+} gbp_fwd_next_t;
+
+always_inline uword
+gbp_fwd_dpo_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame, fib_protocol_t fproto)
+{
+ u32 n_left_from, next_index, *from, *to_next;
+
+ from = vlib_frame_vector_args (from_frame);
+ n_left_from = 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)
+ {
+ const dpo_id_t *next_dpo0;
+ vlib_buffer_t *b0;
+ sclass_t sclass0;
+ u32 bi0, next0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ sclass0 = vnet_buffer2 (b0)->gbp.sclass;
+ next_dpo0 = gbp_epg_dpo_lookup (sclass0, fproto);
+
+ if (PREDICT_TRUE (NULL != next_dpo0))
+ {
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = next_dpo0->dpoi_index;
+ next0 = GBP_FWD_FWD;
+ }
+ else
+ {
+ next0 = GBP_FWD_DROP;
+ }
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ gbp_fwd_dpo_trace_t *tr;
+
+ tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->sclass = sclass0;
+ tr->dpo_index = (NULL != next_dpo0 ?
+ next_dpo0->dpoi_index : ~0);
+ }
+
+ 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);
+ }
+ return from_frame->n_vectors;
+}
+
+static u8 *
+format_gbp_fwd_dpo_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 *);
+ gbp_fwd_dpo_trace_t *t = va_arg (*args, gbp_fwd_dpo_trace_t *);
+
+ s = format (s, " sclass:%d dpo:%d", t->sclass, t->dpo_index);
+
+ return s;
+}
+
+VLIB_NODE_FN (ip4_gbp_fwd_dpo_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return (gbp_fwd_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP4));
+}
+
+VLIB_NODE_FN (ip6_gbp_fwd_dpo_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return (gbp_fwd_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP6));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_gbp_fwd_dpo_node) = {
+ .name = "ip4-gbp-fwd-dpo",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_fwd_dpo_trace,
+ .n_next_nodes = GBP_FWD_N_NEXT,
+ .next_nodes =
+ {
+ [GBP_FWD_DROP] = "ip4-drop",
+ [GBP_FWD_FWD] = "ip4-dvr-dpo",
+ }
+};
+VLIB_REGISTER_NODE (ip6_gbp_fwd_dpo_node) = {
+ .name = "ip6-gbp-fwd-dpo",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_fwd_dpo_trace,
+ .n_next_nodes = GBP_FWD_N_NEXT,
+ .next_nodes =
+ {
+ [GBP_FWD_DROP] = "ip6-drop",
+ [GBP_FWD_FWD] = "ip6-dvr-dpo",
+ }
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_fwd_dpo.h b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.h
new file mode 100644
index 00000000000..6092d6241b5
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_FWD_DPO_H__
+#define __GBP_FWD_DPO_H__
+
+#include <vnet/dpo/dpo.h>
+
+/**
+ * @brief
+ * The GBP FWD DPO. Used in the L3 path to select the correct EPG uplink
+ * based on the source EPG.
+ */
+typedef struct gbp_fwd_dpo_t_
+{
+ /**
+ * The protocol of packets using this DPO
+ */
+ dpo_proto_t gfd_proto;
+
+ /**
+ * number of locks.
+ */
+ u16 gfd_locks;
+} gbp_fwd_dpo_t;
+
+extern void gbp_fwd_dpo_add_or_lock (dpo_proto_t dproto, dpo_id_t * dpo);
+
+extern dpo_type_t gbp_fwd_dpo_get_type (void);
+
+/**
+ * @brief pool of all interface DPOs
+ */
+extern gbp_fwd_dpo_t *gbp_fwd_dpo_pool;
+
+static inline gbp_fwd_dpo_t *
+gbp_fwd_dpo_get (index_t index)
+{
+ return (pool_elt_at_index (gbp_fwd_dpo_pool, index));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
diff --git a/extras/deprecated/plugins/gbp/gbp_fwd_node.c b/extras/deprecated/plugins/gbp/gbp_fwd_node.c
new file mode 100644
index 00000000000..6ea56fd8074
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_fwd_node.c
@@ -0,0 +1,163 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <vnet/l2/l2_input.h>
+
+#define foreach_gbp_fwd \
+ _(DROP, "drop") \
+ _(OUTPUT, "output")
+
+typedef enum
+{
+#define _(sym,str) GBP_FWD_ERROR_##sym,
+ foreach_gbp_fwd
+#undef _
+ GBP_FWD_N_ERROR,
+} gbp_fwd_error_t;
+
+static char *gbp_fwd_error_strings[] = {
+#define _(sym,string) string,
+ foreach_gbp_fwd
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) GBP_FWD_NEXT_##sym,
+ foreach_gbp_fwd
+#undef _
+ GBP_FWD_N_NEXT,
+} gbp_fwd_next_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_fwd_trace_t_
+{
+ /* per-pkt trace data */
+ sclass_t sclass;
+ u32 sw_if_index;
+} gbp_fwd_trace_t;
+
+VLIB_NODE_FN (gbp_fwd_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ u32 next_index;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0, sw_if_index0;
+ gbp_fwd_next_t next0;
+ vlib_buffer_t *b0;
+ sclass_t sclass0;
+
+ next0 = GBP_FWD_NEXT_DROP;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ /*
+ * lookup the uplink based on src EPG
+ */
+ sclass0 = vnet_buffer2 (b0)->gbp.sclass;
+
+ sw_if_index0 = gbp_epg_itf_lookup_sclass (sclass0);
+
+ if (~0 != sw_if_index0)
+ {
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
+
+ next0 = GBP_FWD_NEXT_OUTPUT;
+ }
+ /*
+ * else
+ * don't know the uplink interface for this EPG => drop
+ */
+
+ if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ gbp_fwd_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sclass = sclass0;
+ t->sw_if_index = sw_if_index0;
+ }
+
+ /* 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);
+ }
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_fwd_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 *);
+ gbp_fwd_trace_t *t = va_arg (*args, gbp_fwd_trace_t *);
+
+ s = format (s, "sclass:%d", t->sclass);
+
+ return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_fwd_node) = {
+ .name = "gbp-fwd",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_fwd_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(gbp_fwd_error_strings),
+ .error_strings = gbp_fwd_error_strings,
+
+ .n_next_nodes = GBP_FWD_N_NEXT,
+
+ .next_nodes = {
+ [GBP_FWD_NEXT_DROP] = "error-drop",
+ [GBP_FWD_NEXT_OUTPUT] = "l2-output",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_itf.c b/extras/deprecated/plugins/gbp/gbp_itf.c
new file mode 100644
index 00000000000..738a7ac2e39
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_itf.c
@@ -0,0 +1,574 @@
+/*
+ * 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 <plugins/gbp/gbp_itf.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+
+#include <vnet/ip/ip.h>
+
+#define foreach_gbp_itf_mode \
+ _(L2, "l2") \
+ _(L3, "L3")
+
+typedef enum gbp_ift_mode_t_
+{
+#define _(s,v) GBP_ITF_MODE_##s,
+ foreach_gbp_itf_mode
+#undef _
+} gbp_itf_mode_t;
+
+/**
+ * Attributes and configurations attached to interfaces by GBP
+ */
+typedef struct gbp_itf_t_
+{
+ /**
+ * Number of references to this interface
+ */
+ u32 gi_locks;
+
+ /**
+ * The interface this wrapper is managing
+ */
+ u32 gi_sw_if_index;
+
+ /**
+ * The mode of the interface
+ */
+ gbp_itf_mode_t gi_mode;
+
+ /**
+ * Users of this interface - this is encoded in the user's handle
+ */
+ u32 *gi_users;
+
+ /**
+ * L2/L3 Features configured by each user
+ */
+ u32 *gi_input_fbs;
+ u32 gi_input_fb;
+ u32 *gi_output_fbs;
+ u32 gi_output_fb;
+
+ /**
+ * function to call when the interface is deleted.
+ */
+ gbp_itf_free_fn_t gi_free_fn;
+
+ union
+ {
+ /**
+ * GBP BD or RD index
+ */
+ u32 gi_gbi;
+ index_t gi_gri;
+ };
+} gbp_itf_t;
+
+static gbp_itf_t *gbp_itf_pool;
+static uword *gbp_itf_db;
+
+static const char *gbp_itf_feat_bit_pos_to_arc[] = {
+#define _(s,v,a) [GBP_ITF_L3_FEAT_POS_##s] = a,
+ foreach_gdb_l3_feature
+#undef _
+};
+
+static const char *gbp_itf_feat_bit_pos_to_feat[] = {
+#define _(s,v,a) [GBP_ITF_L3_FEAT_POS_##s] = v,
+ foreach_gdb_l3_feature
+#undef _
+};
+
+u8 *
+format_gbp_itf_l3_feat (u8 * s, va_list * args)
+{
+ gbp_itf_l3_feat_t flags = va_arg (*args, gbp_itf_l3_feat_t);
+
+#define _(a, b, c) \
+ if (flags & GBP_ITF_L3_FEAT_##a) \
+ s = format (s, "%s ", b);
+ foreach_gdb_l3_feature
+#undef _
+ return (s);
+}
+
+void
+gbp_itf_hdl_reset (gbp_itf_hdl_t * gh)
+{
+ *gh = GBP_ITF_HDL_INVALID;
+}
+
+bool
+gbp_itf_hdl_is_valid (gbp_itf_hdl_t gh)
+{
+ return (gh.gh_which != GBP_ITF_HDL_INVALID.gh_which);
+}
+
+static gbp_itf_t *
+gbp_itf_get (index_t gii)
+{
+ if (pool_is_free_index (gbp_itf_pool, gii))
+ return (NULL);
+
+ return (pool_elt_at_index (gbp_itf_pool, gii));
+}
+
+static gbp_itf_t *
+gbp_itf_find (u32 sw_if_index)
+{
+ uword *p;
+
+ p = hash_get (gbp_itf_db, sw_if_index);
+
+ if (NULL != p)
+ return (gbp_itf_get (p[0]));
+
+ return (NULL);
+}
+
+static gbp_itf_t *
+gbp_itf_find_hdl (gbp_itf_hdl_t gh)
+{
+ return (gbp_itf_find (gh.gh_which));
+}
+
+u32
+gbp_itf_get_sw_if_index (gbp_itf_hdl_t hdl)
+{
+ return (hdl.gh_which);
+}
+
+static gbp_itf_hdl_t
+gbp_itf_mk_hdl (gbp_itf_t * gi)
+{
+ gbp_itf_hdl_t gh;
+ u32 *useri;
+
+ pool_get (gi->gi_users, useri);
+ *useri = 0;
+
+ gh.gh_who = useri - gi->gi_users;
+ gh.gh_which = gi->gi_sw_if_index;
+
+ return (gh);
+}
+
+static gbp_itf_hdl_t
+gbp_itf_l2_add_and_lock_i (u32 sw_if_index, index_t gbi, gbp_itf_free_fn_t ff)
+{
+ gbp_itf_t *gi;
+
+ gi = gbp_itf_find (sw_if_index);
+
+ if (NULL == gi)
+ {
+ pool_get_zero (gbp_itf_pool, gi);
+
+ gi->gi_sw_if_index = sw_if_index;
+ gi->gi_gbi = gbi;
+ gi->gi_mode = GBP_ITF_MODE_L2;
+ gi->gi_free_fn = ff;
+
+ gbp_bridge_domain_itf_add (gi->gi_gbi, gi->gi_sw_if_index,
+ L2_BD_PORT_TYPE_NORMAL);
+
+ hash_set (gbp_itf_db, gi->gi_sw_if_index, gi - gbp_itf_pool);
+ }
+
+ gi->gi_locks++;
+
+ return (gbp_itf_mk_hdl (gi));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l2_add_and_lock (u32 sw_if_index, index_t gbi)
+{
+ return (gbp_itf_l2_add_and_lock_i (sw_if_index, gbi, NULL));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l2_add_and_lock_w_free (u32 sw_if_index,
+ index_t gbi, gbp_itf_free_fn_t ff)
+{
+ return (gbp_itf_l2_add_and_lock_i (sw_if_index, gbi, ff));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l3_add_and_lock_i (u32 sw_if_index, index_t gri, gbp_itf_free_fn_t ff)
+{
+ gbp_itf_t *gi;
+
+ gi = gbp_itf_find (sw_if_index);
+
+ if (NULL == gi)
+ {
+ const gbp_route_domain_t *grd;
+ fib_protocol_t fproto;
+
+ pool_get_zero (gbp_itf_pool, gi);
+
+ gi->gi_sw_if_index = sw_if_index;
+ gi->gi_mode = GBP_ITF_MODE_L3;
+ gi->gi_gri = gri;
+ gi->gi_free_fn = ff;
+
+ grd = gbp_route_domain_get (gi->gi_gri);
+
+ ip4_sw_interface_enable_disable (gi->gi_sw_if_index, 1);
+ ip6_sw_interface_enable_disable (gi->gi_sw_if_index, 1);
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ ip_table_bind (fproto, gi->gi_sw_if_index, grd->grd_table_id[fproto]);
+
+ hash_set (gbp_itf_db, gi->gi_sw_if_index, gi - gbp_itf_pool);
+ }
+
+ gi->gi_locks++;
+
+ return (gbp_itf_mk_hdl (gi));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l3_add_and_lock (u32 sw_if_index, index_t gri)
+{
+ return (gbp_itf_l3_add_and_lock_i (sw_if_index, gri, NULL));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l3_add_and_lock_w_free (u32 sw_if_index,
+ index_t gri, gbp_itf_free_fn_t ff)
+{
+ return (gbp_itf_l3_add_and_lock_i (sw_if_index, gri, ff));
+}
+
+void
+gbp_itf_lock (gbp_itf_hdl_t gh)
+{
+ gbp_itf_t *gi;
+
+ if (!gbp_itf_hdl_is_valid (gh))
+ return;
+
+ gi = gbp_itf_find_hdl (gh);
+
+ gi->gi_locks++;
+}
+
+gbp_itf_hdl_t
+gbp_itf_clone_and_lock (gbp_itf_hdl_t gh)
+{
+ gbp_itf_t *gi;
+
+ if (!gbp_itf_hdl_is_valid (gh))
+ return (GBP_ITF_HDL_INVALID);
+
+ gi = gbp_itf_find_hdl (gh);
+
+ gi->gi_locks++;
+
+ return (gbp_itf_mk_hdl (gi));
+}
+
+void
+gbp_itf_unlock (gbp_itf_hdl_t * gh)
+{
+ gbp_itf_t *gi;
+
+ if (!gbp_itf_hdl_is_valid (*gh))
+ return;
+
+ gi = gbp_itf_find_hdl (*gh);
+ ASSERT (gi->gi_locks > 0);
+ gi->gi_locks--;
+
+ if (0 == gi->gi_locks)
+ {
+ if (GBP_ITF_MODE_L2 == gi->gi_mode)
+ {
+ gbp_itf_l2_set_input_feature (*gh, L2INPUT_FEAT_NONE);
+ gbp_itf_l2_set_output_feature (*gh, L2OUTPUT_FEAT_NONE);
+ gbp_bridge_domain_itf_del (gi->gi_gbi,
+ gi->gi_sw_if_index,
+ L2_BD_PORT_TYPE_NORMAL);
+ }
+ else
+ {
+ fib_protocol_t fproto;
+
+ gbp_itf_l3_set_input_feature (*gh, GBP_ITF_L3_FEAT_NONE);
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ ip_table_bind (fproto, gi->gi_sw_if_index, 0);
+
+ ip4_sw_interface_enable_disable (gi->gi_sw_if_index, 0);
+ ip6_sw_interface_enable_disable (gi->gi_sw_if_index, 0);
+ }
+
+ hash_unset (gbp_itf_db, gi->gi_sw_if_index);
+
+ if (gi->gi_free_fn)
+ gi->gi_free_fn (gi->gi_sw_if_index);
+
+ pool_free (gi->gi_users);
+ vec_free (gi->gi_input_fbs);
+ vec_free (gi->gi_output_fbs);
+
+ memset (gi, 0, sizeof (*gi));
+ }
+
+ gbp_itf_hdl_reset (gh);
+}
+
+void
+gbp_itf_l3_set_input_feature (gbp_itf_hdl_t gh, gbp_itf_l3_feat_t feats)
+{
+ u32 diff_fb, new_fb, *fb, feat;
+ gbp_itf_t *gi;
+
+ gi = gbp_itf_find_hdl (gh);
+
+ if (NULL == gi || GBP_ITF_MODE_L3 != gi->gi_mode)
+ return;
+
+ vec_validate (gi->gi_input_fbs, gh.gh_who);
+ gi->gi_input_fbs[gh.gh_who] = feats;
+
+ new_fb = 0;
+ vec_foreach (fb, gi->gi_input_fbs)
+ {
+ new_fb |= *fb;
+ }
+
+ /* add new features */
+ diff_fb = (gi->gi_input_fb ^ new_fb) & new_fb;
+
+ /* *INDENT-OFF* */
+ foreach_set_bit (feat, diff_fb,
+ ({
+ vnet_feature_enable_disable (gbp_itf_feat_bit_pos_to_arc[feat],
+ gbp_itf_feat_bit_pos_to_feat[feat],
+ gi->gi_sw_if_index, 1, 0, 0);
+ }));
+ /* *INDENT-ON* */
+
+ /* remove unneeded features */
+ diff_fb = (gi->gi_input_fb ^ new_fb) & gi->gi_input_fb;
+
+ /* *INDENT-OFF* */
+ foreach_set_bit (feat, diff_fb,
+ ({
+ vnet_feature_enable_disable (gbp_itf_feat_bit_pos_to_arc[feat],
+ gbp_itf_feat_bit_pos_to_feat[feat],
+ gi->gi_sw_if_index, 0, 0, 0);
+ }));
+ /* *INDENT-ON* */
+
+ gi->gi_input_fb = new_fb;
+}
+
+void
+gbp_itf_l2_set_input_feature (gbp_itf_hdl_t gh, l2input_feat_masks_t feats)
+{
+ u32 diff_fb, new_fb, *fb, feat;
+ gbp_itf_t *gi;
+
+ gi = gbp_itf_find_hdl (gh);
+
+ if (NULL == gi || GBP_ITF_MODE_L2 != gi->gi_mode)
+ {
+ ASSERT (0);
+ return;
+ }
+
+ vec_validate (gi->gi_input_fbs, gh.gh_who);
+ gi->gi_input_fbs[gh.gh_who] = feats;
+
+ new_fb = 0;
+ vec_foreach (fb, gi->gi_input_fbs)
+ {
+ new_fb |= *fb;
+ }
+
+ /* add new features */
+ diff_fb = (gi->gi_input_fb ^ new_fb) & new_fb;
+
+ /* *INDENT-OFF* */
+ foreach_set_bit (feat, diff_fb,
+ ({
+ l2input_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 1);
+ }));
+ /* *INDENT-ON* */
+
+ /* remove unneeded features */
+ diff_fb = (gi->gi_input_fb ^ new_fb) & gi->gi_input_fb;
+
+ /* *INDENT-OFF* */
+ foreach_set_bit (feat, diff_fb,
+ ({
+ l2input_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 0);
+ }));
+ /* *INDENT-ON* */
+
+ gi->gi_input_fb = new_fb;
+}
+
+void
+gbp_itf_l2_set_output_feature (gbp_itf_hdl_t gh, l2output_feat_masks_t feats)
+{
+ u32 diff_fb, new_fb, *fb, feat;
+ gbp_itf_t *gi;
+
+ gi = gbp_itf_find_hdl (gh);
+
+ if (NULL == gi || GBP_ITF_MODE_L2 != gi->gi_mode)
+ {
+ ASSERT (0);
+ return;
+ }
+
+ vec_validate (gi->gi_output_fbs, gh.gh_who);
+ gi->gi_output_fbs[gh.gh_who] = feats;
+
+ new_fb = 0;
+ vec_foreach (fb, gi->gi_output_fbs)
+ {
+ new_fb |= *fb;
+ }
+
+ /* add new features */
+ diff_fb = (gi->gi_output_fb ^ new_fb) & new_fb;
+
+ /* *INDENT-OFF* */
+ foreach_set_bit (feat, diff_fb,
+ ({
+ l2output_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 1);
+ }));
+ /* *INDENT-ON* */
+
+ /* remove unneeded features */
+ diff_fb = (gi->gi_output_fb ^ new_fb) & gi->gi_output_fb;
+
+ /* *INDENT-OFF* */
+ foreach_set_bit (feat, diff_fb,
+ ({
+ l2output_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 0);
+ }));
+ /* *INDENT-ON* */
+
+ gi->gi_output_fb = new_fb;
+}
+
+static u8 *
+format_gbp_itf_mode (u8 * s, va_list * args)
+{
+ gbp_itf_mode_t mode = va_arg (*args, gbp_itf_mode_t);
+
+ switch (mode)
+ {
+#define _(a,v) \
+ case GBP_ITF_MODE_##a: \
+ return format(s, "%s", v);
+ foreach_gbp_itf_mode
+#undef _
+ }
+ return (s);
+}
+
+static u8 *
+format_gbp_itf (u8 * s, va_list * args)
+{
+ index_t gii = va_arg (*args, index_t);
+ gbp_itf_t *gi;
+
+ if (INDEX_INVALID == gii)
+ return (format (s, "unset"));
+
+ gi = gbp_itf_get (gii);
+
+ s = format (s, "%U locks:%d mode:%U ",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ gi->gi_sw_if_index, gi->gi_locks,
+ format_gbp_itf_mode, gi->gi_mode);
+
+ if (GBP_ITF_MODE_L2 == gi->gi_mode)
+ s = format (s, "gbp-bd:%d input-feats:[%U] output-feats:[%U]",
+ gi->gi_gbi,
+ format_l2_input_features, gi->gi_input_fb, 0,
+ format_l2_output_features, gi->gi_output_fb, 0);
+ else
+ s = format (s, "gbp-rd:%d input-feats:[%U] output-feats:[%U]",
+ gi->gi_gbi,
+ format_gbp_itf_l3_feat, gi->gi_input_fb,
+ format_gbp_itf_l3_feat, gi->gi_output_fb);
+
+ return (s);
+}
+
+u8 *
+format_gbp_itf_hdl (u8 * s, va_list * args)
+{
+ gbp_itf_hdl_t gh = va_arg (*args, gbp_itf_hdl_t);
+ gbp_itf_t *gi;
+
+ gi = gbp_itf_find_hdl (gh);
+
+ if (NULL == gi)
+ return format (s, "INVALID");
+
+ return (format (s, "%U", format_gbp_itf, gi - gbp_itf_pool));
+}
+
+static clib_error_t *
+gbp_itf_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ u32 gii;
+
+ vlib_cli_output (vm, "Interfaces:");
+
+ /* *INDENT-OFF* */
+ pool_foreach_index (gii, gbp_itf_pool)
+ {
+ vlib_cli_output (vm, " [%d] %U", gii, format_gbp_itf, gii);
+ }
+ /* *INDENT-ON* */
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Interfaces
+ *
+ * @cliexpar
+ * @cliexstart{show gbp contract}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
+ .path = "show gbp interface",
+ .short_help = "show gbp interface\n",
+ .function = gbp_itf_show,
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_itf.h b/extras/deprecated/plugins/gbp/gbp_itf.h
new file mode 100644
index 00000000000..23a09b2a9ff
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_itf.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_INTERFACE_H__
+#define __GBP_INTERFACE_H__
+
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
+#include <vnet/dpo/dpo.h>
+
+
+#define foreach_gdb_l3_feature \
+ _(LEARN_IP4, "gbp-learn-ip4", "ip4-unicast") \
+ _(LEARN_IP6, "gbp-learn-ip6", "ip6-unicast")
+
+typedef enum gbp_itf_l3_feat_pos_t_
+{
+#define _(s,v,a) GBP_ITF_L3_FEAT_POS_##s,
+ foreach_gdb_l3_feature
+#undef _
+} gbp_itf_l3_feat_pos_t;
+
+typedef enum gbp_itf_l3_feat_t_
+{
+ GBP_ITF_L3_FEAT_NONE,
+#define _(s,v,a) GBP_ITF_L3_FEAT_##s = (1 << GBP_ITF_L3_FEAT_POS_##s),
+ foreach_gdb_l3_feature
+#undef _
+} gbp_itf_l3_feat_t;
+
+#define GBP_ITF_L3_FEAT_LEARN (GBP_ITF_L3_FEAT_LEARN_IP4|GBP_ITF_L3_FEAT_LEARN_IP6)
+
+typedef struct gbp_itf_hdl_t_
+{
+ union
+ {
+ struct
+ {
+ u32 gh_who;
+ u32 gh_which;
+ };
+ };
+} gbp_itf_hdl_t;
+
+#define GBP_ITF_HDL_INIT {.gh_which = ~0}
+const static gbp_itf_hdl_t GBP_ITF_HDL_INVALID = GBP_ITF_HDL_INIT;
+
+extern void gbp_itf_hdl_reset (gbp_itf_hdl_t * gh);
+extern bool gbp_itf_hdl_is_valid (gbp_itf_hdl_t gh);
+
+typedef void (*gbp_itf_free_fn_t) (u32 sw_if_index);
+
+extern gbp_itf_hdl_t gbp_itf_l2_add_and_lock (u32 sw_if_index, u32 bd_index);
+extern gbp_itf_hdl_t gbp_itf_l3_add_and_lock (u32 sw_if_index, index_t gri);
+extern gbp_itf_hdl_t gbp_itf_l2_add_and_lock_w_free (u32 sw_if_index,
+ u32 bd_index,
+ gbp_itf_free_fn_t ff);
+extern gbp_itf_hdl_t gbp_itf_l3_add_and_lock_w_free (u32 sw_if_index,
+ index_t gri,
+ gbp_itf_free_fn_t ff);
+
+extern void gbp_itf_unlock (gbp_itf_hdl_t * hdl);
+extern void gbp_itf_lock (gbp_itf_hdl_t hdl);
+extern gbp_itf_hdl_t gbp_itf_clone_and_lock (gbp_itf_hdl_t hdl);
+extern u32 gbp_itf_get_sw_if_index (gbp_itf_hdl_t hdl);
+
+extern void gbp_itf_l2_set_input_feature (gbp_itf_hdl_t hdl,
+ l2input_feat_masks_t feats);
+extern void gbp_itf_l2_set_output_feature (gbp_itf_hdl_t hdl,
+ l2output_feat_masks_t feats);
+
+extern void gbp_itf_l3_set_input_feature (gbp_itf_hdl_t hdl,
+ gbp_itf_l3_feat_t feats);
+
+extern u8 *format_gbp_itf_hdl (u8 * s, va_list * args);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_learn.c b/extras/deprecated/plugins/gbp/gbp_learn.c
new file mode 100644
index 00000000000..af3a6fb52ac
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_learn.c
@@ -0,0 +1,76 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+
+#include <vnet/l2/l2_input.h>
+
+gbp_learn_main_t gbp_learn_main;
+
+void
+gbp_learn_enable (u32 sw_if_index)
+{
+ vnet_feature_enable_disable ("ip4-unicast",
+ "gbp-learn-ip4", sw_if_index, 1, 0, 0);
+ vnet_feature_enable_disable ("ip6-unicast",
+ "gbp-learn-ip6", sw_if_index, 1, 0, 0);
+}
+
+void
+gbp_learn_disable (u32 sw_if_index)
+{
+ vnet_feature_enable_disable ("ip4-unicast",
+ "gbp-learn-ip4", sw_if_index, 0, 0, 0);
+ vnet_feature_enable_disable ("ip6-unicast",
+ "gbp-learn-ip6", sw_if_index, 0, 0, 0);
+}
+
+static clib_error_t *
+gbp_learn_init (vlib_main_t * vm)
+{
+ gbp_learn_main_t *glm = &gbp_learn_main;
+ vlib_thread_main_t *tm = &vlib_thread_main;
+
+ vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-learn-l2");
+
+ /* Initialize the feature next-node indices */
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ glm->gl_l2_input_feat_next);
+
+ throttle_init (&glm->gl_l2_throttle,
+ tm->n_vlib_mains, GBP_ENDPOINT_HASH_LEARN_RATE);
+
+ throttle_init (&glm->gl_l3_throttle,
+ tm->n_vlib_mains, GBP_ENDPOINT_HASH_LEARN_RATE);
+
+ glm->gl_logger = vlib_log_register_class ("gbp", "learn");
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_learn_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_learn.h b/extras/deprecated/plugins/gbp/gbp_learn.h
new file mode 100644
index 00000000000..b4f3ae0a23d
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_learn.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_LEARN_H__
+#define __GBP_LEARN_H__
+
+#include <plugins/gbp/gbp.h>
+
+#include <vnet/util/throttle.h>
+
+/**
+ * The maximum learning rate per-hashed EP
+ */
+#define GBP_ENDPOINT_HASH_LEARN_RATE (1e-2)
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_learn_main_t_
+{
+ /**
+ * Next nodes for L2 output features
+ */
+ u32 gl_l2_input_feat_next[32];
+
+ /**
+ * logger - VLIB log class
+ */
+ vlib_log_class_t gl_logger;
+
+ /**
+ * throttles for the DP leanring
+ */
+ throttle_t gl_l2_throttle;
+ throttle_t gl_l3_throttle;
+} gbp_learn_main_t;
+
+extern gbp_learn_main_t gbp_learn_main;
+
+extern void gbp_learn_enable (u32 sw_if_index);
+extern void gbp_learn_disable (u32 sw_if_index);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_learn_node.c b/extras/deprecated/plugins/gbp/gbp_learn_node.c
new file mode 100644
index 00000000000..a6c54971956
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_learn_node.c
@@ -0,0 +1,718 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/util/throttle.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+#include <vnet/ethernet/arp_packet.h>
+
+#define GBP_LEARN_DBG(...) \
+ vlib_log_debug (gbp_learn_main.gl_logger, __VA_ARGS__);
+
+#define foreach_gbp_learn \
+ _(DROP, "drop")
+
+typedef enum
+{
+#define _(sym,str) GBP_LEARN_ERROR_##sym,
+ foreach_gbp_learn
+#undef _
+ GBP_LEARN_N_ERROR,
+} gbp_learn_error_t;
+
+static char *gbp_learn_error_strings[] = {
+#define _(sym,string) string,
+ foreach_gbp_learn
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) GBP_LEARN_NEXT_##sym,
+ foreach_gbp_learn
+#undef _
+ GBP_LEARN_N_NEXT,
+} gbp_learn_next_t;
+
+typedef struct gbp_learn_l2_t_
+{
+ ip46_address_t ip;
+ mac_address_t mac;
+ u32 sw_if_index;
+ u32 bd_index;
+ sclass_t sclass;
+ ip46_address_t outer_src;
+ ip46_address_t outer_dst;
+} gbp_learn_l2_t;
+
+
+static void
+gbp_learn_l2_cp (const gbp_learn_l2_t * gl2)
+{
+ ip46_address_t *ips = NULL;
+
+ GBP_LEARN_DBG ("L2 EP: %U %U, %d",
+ format_mac_address_t, &gl2->mac,
+ format_ip46_address, &gl2->ip, IP46_TYPE_ANY, gl2->sclass);
+
+ if (!ip46_address_is_zero (&gl2->ip))
+ vec_add1 (ips, gl2->ip);
+
+ /*
+ * flip the source and dst, since that's how it was received, this API
+ * takes how it's sent
+ */
+ gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_DP,
+ gl2->sw_if_index, ips,
+ &gl2->mac, INDEX_INVALID,
+ INDEX_INVALID, gl2->sclass,
+ (GBP_ENDPOINT_FLAG_LEARNT |
+ GBP_ENDPOINT_FLAG_REMOTE),
+ &gl2->outer_dst, &gl2->outer_src, NULL);
+ vec_free (ips);
+}
+
+static void
+gbp_learn_l2_ip4_dp (const u8 * mac, const ip4_address_t * ip,
+ u32 bd_index, u32 sw_if_index, sclass_t sclass,
+ const ip4_address_t * outer_src,
+ const ip4_address_t * outer_dst)
+{
+ gbp_learn_l2_t gl2 = {
+ .sw_if_index = sw_if_index,
+ .bd_index = bd_index,
+ .sclass = sclass,
+ .ip.ip4 = *ip,
+ .outer_src.ip4 = *outer_src,
+ .outer_dst.ip4 = *outer_dst,
+ };
+ mac_address_from_bytes (&gl2.mac, mac);
+
+ vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
+}
+
+static void
+gbp_learn_l2_ip6_dp (const u8 * mac, const ip6_address_t * ip,
+ u32 bd_index, u32 sw_if_index, sclass_t sclass,
+ const ip4_address_t * outer_src,
+ const ip4_address_t * outer_dst)
+{
+ gbp_learn_l2_t gl2 = {
+ .sw_if_index = sw_if_index,
+ .bd_index = bd_index,
+ .sclass = sclass,
+ .ip.ip6 = *ip,
+ .outer_src.ip4 = *outer_src,
+ .outer_dst.ip4 = *outer_dst,
+ };
+ mac_address_from_bytes (&gl2.mac, mac);
+
+ vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
+}
+
+static void
+gbp_learn_l2_dp (const u8 * mac, u32 bd_index, u32 sw_if_index,
+ sclass_t sclass,
+ const ip4_address_t * outer_src,
+ const ip4_address_t * outer_dst)
+{
+ gbp_learn_l2_t gl2 = {
+ .sw_if_index = sw_if_index,
+ .bd_index = bd_index,
+ .sclass = sclass,
+ .outer_src.ip4 = *outer_src,
+ .outer_dst.ip4 = *outer_dst,
+ };
+ mac_address_from_bytes (&gl2.mac, mac);
+
+ vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
+}
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_learn_l2_trace_t_
+{
+ /* per-pkt trace data */
+ mac_address_t mac;
+ u32 sw_if_index;
+ u32 new;
+ u32 throttled;
+ u32 sclass;
+ u32 d_bit;
+ gbp_bridge_domain_flags_t gb_flags;
+} gbp_learn_l2_trace_t;
+
+always_inline void
+gbp_learn_get_outer (const ethernet_header_t * eh0,
+ ip4_address_t * outer_src, ip4_address_t * outer_dst)
+{
+ ip4_header_t *ip0;
+ u8 *buff;
+
+ /* rewind back to the ivxlan header */
+ buff = (u8 *) eh0;
+ buff -= (sizeof (vxlan_gbp_header_t) +
+ sizeof (udp_header_t) + sizeof (ip4_header_t));
+
+ ip0 = (ip4_header_t *) buff;
+
+ *outer_src = ip0->src_address;
+ *outer_dst = ip0->dst_address;
+}
+
+always_inline int
+gbp_endpoint_update_required (const gbp_endpoint_t * ge0,
+ u32 rx_sw_if_index, sclass_t sclass)
+{
+ /* Conditions for [re]learning this EP */
+
+ /* 1. it doesn't have a dataplane source */
+ if (!gbp_endpoint_is_learnt (ge0))
+ return (!0);
+
+ /* 2. has the input interface changed */
+ if (gbp_itf_get_sw_if_index (ge0->ge_fwd.gef_itf) != rx_sw_if_index)
+ return (!0);
+
+ /* 3. has the sclass changed */
+ if (sclass != ge0->ge_fwd.gef_sclass)
+ return (!0);
+
+ /* otherwise it's unchanged */
+ return (0);
+}
+
+VLIB_NODE_FN (gbp_learn_l2_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next, next_index, thread_index, seed;
+ gbp_learn_main_t *glm;
+ f64 time_now;
+
+ glm = &gbp_learn_main;
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+ time_now = vlib_time_now (vm);
+ thread_index = vm->thread_index;
+
+ seed = throttle_seed (&glm->gl_l2_throttle, thread_index, time_now);
+
+ 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)
+ {
+ ip4_address_t outer_src, outer_dst;
+ const ethernet_header_t *eh0;
+ u32 bi0, sw_if_index0, t0;
+ gbp_bridge_domain_t *gb0;
+ gbp_learn_next_t next0;
+ gbp_endpoint_t *ge0;
+ vlib_buffer_t *b0;
+ sclass_t sclass0;
+
+ next0 = GBP_LEARN_NEXT_DROP;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+ eh0 = vlib_buffer_get_current (b0);
+ sclass0 = vnet_buffer2 (b0)->gbp.sclass;
+
+ next0 = vnet_l2_feature_next (b0, glm->gl_l2_input_feat_next,
+ L2INPUT_FEAT_GBP_LEARN);
+
+ ge0 = gbp_endpoint_find_mac (eh0->src_address,
+ vnet_buffer (b0)->l2.bd_index);
+ gb0 =
+ gbp_bridge_domain_get_by_bd_index (vnet_buffer (b0)->l2.bd_index);
+
+ if ((vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D) ||
+ (gb0->gb_flags & GBP_BD_FLAG_DO_NOT_LEARN))
+ {
+ t0 = 1;
+ goto trace;
+ }
+
+ /*
+ * check for new EP or a moved EP
+ */
+ if (NULL == ge0 ||
+ gbp_endpoint_update_required (ge0, sw_if_index0, sclass0))
+ {
+ /*
+ * use the last 4 bytes of the mac address as the hash for the EP
+ */
+ t0 = throttle_check (&glm->gl_l2_throttle, thread_index,
+ *((u32 *) (eh0->src_address + 2)), seed);
+ if (!t0)
+ {
+ gbp_learn_get_outer (eh0, &outer_src, &outer_dst);
+
+ if (outer_src.as_u32 == 0 || outer_dst.as_u32 == 0)
+ {
+ t0 = 2;
+ goto trace;
+ }
+
+ switch (clib_net_to_host_u16 (eh0->type))
+ {
+ case ETHERNET_TYPE_IP4:
+ {
+ const ip4_header_t *ip0;
+
+ ip0 = (ip4_header_t *) (eh0 + 1);
+
+ gbp_learn_l2_ip4_dp (eh0->src_address,
+ &ip0->src_address,
+ vnet_buffer (b0)->l2.bd_index,
+ sw_if_index0, sclass0,
+ &outer_src, &outer_dst);
+
+ break;
+ }
+ case ETHERNET_TYPE_IP6:
+ {
+ const ip6_header_t *ip0;
+
+ ip0 = (ip6_header_t *) (eh0 + 1);
+
+ gbp_learn_l2_ip6_dp (eh0->src_address,
+ &ip0->src_address,
+ vnet_buffer (b0)->l2.bd_index,
+ sw_if_index0, sclass0,
+ &outer_src, &outer_dst);
+
+ break;
+ }
+ case ETHERNET_TYPE_ARP:
+ {
+ const ethernet_arp_header_t *arp0;
+
+ arp0 = (ethernet_arp_header_t *) (eh0 + 1);
+
+ gbp_learn_l2_ip4_dp (eh0->src_address,
+ &arp0->ip4_over_ethernet[0].ip4,
+ vnet_buffer (b0)->l2.bd_index,
+ sw_if_index0, sclass0,
+ &outer_src, &outer_dst);
+ break;
+ }
+ default:
+ gbp_learn_l2_dp (eh0->src_address,
+ vnet_buffer (b0)->l2.bd_index,
+ sw_if_index0, sclass0,
+ &outer_src, &outer_dst);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * this update could happen simultaneoulsy from multiple workers
+ * but that's ok we are not interested in being very accurate.
+ */
+ t0 = 0;
+ ge0->ge_last_time = time_now;
+ }
+ trace:
+ if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ gbp_learn_l2_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ clib_memcpy_fast (t->mac.bytes, eh0->src_address, 6);
+ t->new = (NULL == ge0);
+ t->throttled = t0;
+ t->sw_if_index = sw_if_index0;
+ t->sclass = sclass0;
+ t->gb_flags = gb0->gb_flags;
+ t->d_bit = ! !(vnet_buffer2 (b0)->gbp.flags &
+ VXLAN_GBP_GPFLAGS_D);
+ }
+
+ /* 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);
+ }
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_learn_l2_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 *);
+ gbp_learn_l2_trace_t *t = va_arg (*args, gbp_learn_l2_trace_t *);
+
+ s = format (s, "new:%d throttled:%d d-bit:%d mac:%U itf:%d sclass:%d"
+ " gb-flags:%U",
+ t->new, t->throttled, t->d_bit,
+ format_mac_address_t, &t->mac, t->sw_if_index, t->sclass,
+ format_gbp_bridge_domain_flags, t->gb_flags);
+
+ return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_learn_l2_node) = {
+ .name = "gbp-learn-l2",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_learn_l2_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(gbp_learn_error_strings),
+ .error_strings = gbp_learn_error_strings,
+
+ .n_next_nodes = GBP_LEARN_N_NEXT,
+
+ .next_nodes = {
+ [GBP_LEARN_NEXT_DROP] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+typedef struct gbp_learn_l3_t_
+{
+ ip46_address_t ip;
+ u32 fib_index;
+ u32 sw_if_index;
+ sclass_t sclass;
+ ip46_address_t outer_src;
+ ip46_address_t outer_dst;
+} gbp_learn_l3_t;
+
+static void
+gbp_learn_l3_cp (const gbp_learn_l3_t * gl3)
+{
+ ip46_address_t *ips = NULL;
+
+ GBP_LEARN_DBG ("L3 EP: %U, %d", format_ip46_address, &gl3->ip,
+ IP46_TYPE_ANY, gl3->sclass);
+
+ vec_add1 (ips, gl3->ip);
+
+ gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_DP,
+ gl3->sw_if_index, ips, NULL,
+ INDEX_INVALID, INDEX_INVALID, gl3->sclass,
+ (GBP_ENDPOINT_FLAG_REMOTE |
+ GBP_ENDPOINT_FLAG_LEARNT),
+ &gl3->outer_dst, &gl3->outer_src, NULL);
+ vec_free (ips);
+}
+
+static void
+gbp_learn_ip4_dp (const ip4_address_t * ip,
+ u32 fib_index, u32 sw_if_index, sclass_t sclass,
+ const ip4_address_t * outer_src,
+ const ip4_address_t * outer_dst)
+{
+ /* *INDENT-OFF* */
+ gbp_learn_l3_t gl3 = {
+ .ip = {
+ .ip4 = *ip,
+ },
+ .sw_if_index = sw_if_index,
+ .fib_index = fib_index,
+ .sclass = sclass,
+ .outer_src.ip4 = *outer_src,
+ .outer_dst.ip4 = *outer_dst,
+ };
+ /* *INDENT-ON* */
+
+ vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3));
+}
+
+static void
+gbp_learn_ip6_dp (const ip6_address_t * ip,
+ u32 fib_index, u32 sw_if_index, sclass_t sclass,
+ const ip4_address_t * outer_src,
+ const ip4_address_t * outer_dst)
+{
+ /* *INDENT-OFF* */
+ gbp_learn_l3_t gl3 = {
+ .ip = {
+ .ip6 = *ip,
+ },
+ .sw_if_index = sw_if_index,
+ .fib_index = fib_index,
+ .sclass = sclass,
+ .outer_src.ip4 = *outer_src,
+ .outer_dst.ip4 = *outer_dst,
+ };
+ /* *INDENT-ON* */
+
+ vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3));
+}
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_learn_l3_trace_t_
+{
+ /* per-pkt trace data */
+ ip46_address_t ip;
+ u32 sw_if_index;
+ u32 new;
+ u32 throttled;
+ u32 sclass;
+} gbp_learn_l3_trace_t;
+
+static uword
+gbp_learn_l3 (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame,
+ fib_protocol_t fproto)
+{
+ u32 n_left_from, *from, *to_next, next_index, thread_index, seed;
+ gbp_learn_main_t *glm;
+ f64 time_now;
+
+ glm = &gbp_learn_main;
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+ time_now = vlib_time_now (vm);
+ thread_index = vm->thread_index;
+
+ seed = throttle_seed (&glm->gl_l3_throttle, thread_index, time_now);
+
+ 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)
+ {
+ CLIB_UNUSED (const ip4_header_t *) ip4_0;
+ CLIB_UNUSED (const ip6_header_t *) ip6_0;
+ u32 bi0, sw_if_index0, t0, fib_index0;
+ ip4_address_t outer_src, outer_dst;
+ ethernet_header_t *eth0;
+ gbp_learn_next_t next0;
+ gbp_endpoint_t *ge0;
+ vlib_buffer_t *b0;
+ sclass_t sclass0;
+
+ next0 = GBP_LEARN_NEXT_DROP;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ sclass0 = vnet_buffer2 (b0)->gbp.sclass;
+ ip6_0 = NULL;
+ ip4_0 = NULL;
+
+ vnet_feature_next (&next0, b0);
+
+ if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D)
+ {
+ t0 = 1;
+ ge0 = NULL;
+ goto trace;
+ }
+
+ fib_index0 = fib_table_get_index_for_sw_if_index (fproto,
+ sw_if_index0);
+
+ if (FIB_PROTOCOL_IP6 == fproto)
+ {
+ ip6_0 = vlib_buffer_get_current (b0);
+ eth0 = (ethernet_header_t *) (((u8 *) ip6_0) - sizeof (*eth0));
+
+ gbp_learn_get_outer (eth0, &outer_src, &outer_dst);
+
+ ge0 = gbp_endpoint_find_ip6 (&ip6_0->src_address, fib_index0);
+
+ if ((NULL == ge0) ||
+ gbp_endpoint_update_required (ge0, sw_if_index0, sclass0))
+ {
+ t0 = throttle_check (&glm->gl_l3_throttle,
+ thread_index,
+ ip6_address_hash_to_u32
+ (&ip6_0->src_address), seed);
+
+ if (!t0)
+ {
+ gbp_learn_ip6_dp (&ip6_0->src_address,
+ fib_index0, sw_if_index0, sclass0,
+ &outer_src, &outer_dst);
+ }
+ }
+ else
+ {
+ /*
+ * this update could happen simultaneoulsy from multiple
+ * workers but that's ok we are not interested in being
+ * very accurate.
+ */
+ t0 = 0;
+ ge0->ge_last_time = time_now;
+ }
+ }
+ else
+ {
+ ip4_0 = vlib_buffer_get_current (b0);
+ eth0 = (ethernet_header_t *) (((u8 *) ip4_0) - sizeof (*eth0));
+
+ gbp_learn_get_outer (eth0, &outer_src, &outer_dst);
+ ge0 = gbp_endpoint_find_ip4 (&ip4_0->src_address, fib_index0);
+
+ if ((NULL == ge0) ||
+ gbp_endpoint_update_required (ge0, sw_if_index0, sclass0))
+ {
+ t0 = throttle_check (&glm->gl_l3_throttle, thread_index,
+ ip4_0->src_address.as_u32, seed);
+
+ if (!t0)
+ {
+ gbp_learn_ip4_dp (&ip4_0->src_address,
+ fib_index0, sw_if_index0, sclass0,
+ &outer_src, &outer_dst);
+ }
+ }
+ else
+ {
+ /*
+ * this update could happen simultaneoulsy from multiple
+ * workers but that's ok we are not interested in being
+ * very accurate.
+ */
+ t0 = 0;
+ ge0->ge_last_time = time_now;
+ }
+ }
+ trace:
+ if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ gbp_learn_l3_trace_t *t;
+
+ t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ if (FIB_PROTOCOL_IP6 == fproto && ip6_0)
+ ip46_address_set_ip6 (&t->ip, &ip6_0->src_address);
+ if (FIB_PROTOCOL_IP4 == fproto && ip4_0)
+ ip46_address_set_ip4 (&t->ip, &ip4_0->src_address);
+ t->new = (NULL == ge0);
+ t->throttled = t0;
+ t->sw_if_index = sw_if_index0;
+ t->sclass = sclass0;
+ }
+
+ 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);
+ }
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_learn_l3_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 *);
+ gbp_learn_l3_trace_t *t = va_arg (*args, gbp_learn_l3_trace_t *);
+
+ s = format (s, "new:%d throttled:%d ip:%U itf:%d sclass:%d",
+ t->new, t->throttled,
+ format_ip46_address, &t->ip, IP46_TYPE_ANY, t->sw_if_index,
+ t->sclass);
+
+ return s;
+}
+
+VLIB_NODE_FN (gbp_learn_ip4_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP4));
+}
+
+VLIB_NODE_FN (gbp_learn_ip6_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP6));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_learn_ip4_node) = {
+ .name = "gbp-learn-ip4",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_learn_l3_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+};
+
+VNET_FEATURE_INIT (gbp_learn_ip4, static) =
+{
+ .arc_name = "ip4-unicast",
+ .node_name = "gbp-learn-ip4",
+};
+
+VLIB_REGISTER_NODE (gbp_learn_ip6_node) = {
+ .name = "gbp-learn-ip6",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_learn_l3_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+};
+
+VNET_FEATURE_INIT (gbp_learn_ip6, static) =
+{
+ .arc_name = "ip6-unicast",
+ .node_name = "gbp-learn-ip6",
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_policy.c b/extras/deprecated/plugins/gbp/gbp_policy.c
new file mode 100644
index 00000000000..127c6d3f059
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_policy.c
@@ -0,0 +1,79 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_policy.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+
+gbp_policy_main_t gbp_policy_main;
+
+/* packet trace format function */
+u8 *
+format_gbp_policy_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 *);
+ gbp_policy_trace_t *t = va_arg (*args, gbp_policy_trace_t *);
+
+ s =
+ format (s,
+ "scope:%d sclass:%d, dclass:%d, action:%U flags:%U acl: %d rule: %d",
+ t->scope, t->sclass, t->dclass, format_gbp_rule_action, t->action,
+ format_vxlan_gbp_header_gpflags, t->flags, t->acl_match,
+ t->rule_match);
+
+ return s;
+}
+
+static clib_error_t *
+gbp_policy_init (vlib_main_t * vm)
+{
+ gbp_policy_main_t *gpm = &gbp_policy_main;
+ clib_error_t *error = 0;
+
+ /* Initialize the feature next-node indexes */
+ vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-port");
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2OUTPUT_N_FEAT,
+ l2output_get_feat_names (),
+ gpm->l2_output_feat_next[GBP_POLICY_PORT]);
+
+ node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-mac");
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2OUTPUT_N_FEAT,
+ l2output_get_feat_names (),
+ gpm->l2_output_feat_next[GBP_POLICY_MAC]);
+
+ node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-lpm");
+ feat_bitmap_init_next_nodes (vm,
+ node->index,
+ L2OUTPUT_N_FEAT,
+ l2output_get_feat_names (),
+ gpm->l2_output_feat_next[GBP_POLICY_LPM]);
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (gbp_policy_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_policy.h b/extras/deprecated/plugins/gbp/gbp_policy.h
new file mode 100644
index 00000000000..6f87f2ec7c4
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_policy.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_POLICY_H__
+#define __GBP_POLICY_H__
+
+#include <plugins/gbp/gbp_contract.h>
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_policy_trace_t_
+{
+ /* per-pkt trace data */
+ gbp_scope_t scope;
+ sclass_t sclass;
+ sclass_t dclass;
+ gbp_rule_action_t action;
+ u32 flags;
+ u32 acl_match;
+ u32 rule_match;
+} gbp_policy_trace_t;
+
+/* packet trace format function */
+u8 * format_gbp_policy_trace (u8 * s, va_list * args);
+
+static_always_inline void
+gbp_policy_trace(vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t *b, const gbp_contract_key_t *key, gbp_rule_action_t action, u32 acl_match, u32 rule_match)
+{
+ gbp_policy_trace_t *t;
+
+ if (PREDICT_TRUE (!(b->flags & VLIB_BUFFER_IS_TRACED)))
+ return;
+
+ t = vlib_add_trace (vm, node, b, sizeof (*t));
+ t->sclass = key->gck_src;
+ t->dclass = key->gck_dst;
+ t->scope = key->gck_scope;
+ t->action = action;
+ t->flags = vnet_buffer2 (b)->gbp.flags;
+ t->acl_match = acl_match;
+ t->rule_match = rule_match;
+}
+
+#endif /* __GBP_POLICY_H__ */
diff --git a/extras/deprecated/plugins/gbp/gbp_policy_dpo.c b/extras/deprecated/plugins/gbp/gbp_policy_dpo.c
new file mode 100644
index 00000000000..9f26b9c67ab
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_policy_dpo.c
@@ -0,0 +1,420 @@
+/*
+ * 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 <vnet/dpo/dvr_dpo.h>
+#include <vnet/dpo/drop_dpo.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+
+#include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_policy.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_recirc.h>
+#include <plugins/gbp/gbp_contract.h>
+
+#ifndef CLIB_MARCH_VARIANT
+/**
+ * DPO pool
+ */
+gbp_policy_dpo_t *gbp_policy_dpo_pool;
+
+/**
+ * DPO type registered for these GBP FWD
+ */
+dpo_type_t gbp_policy_dpo_type;
+
+static gbp_policy_dpo_t *
+gbp_policy_dpo_alloc (void)
+{
+ gbp_policy_dpo_t *gpd;
+
+ pool_get_aligned_zero (gbp_policy_dpo_pool, gpd, CLIB_CACHE_LINE_BYTES);
+
+ return (gpd);
+}
+
+static inline gbp_policy_dpo_t *
+gbp_policy_dpo_get_from_dpo (const dpo_id_t * dpo)
+{
+ ASSERT (gbp_policy_dpo_type == dpo->dpoi_type);
+
+ return (gbp_policy_dpo_get (dpo->dpoi_index));
+}
+
+static inline index_t
+gbp_policy_dpo_get_index (gbp_policy_dpo_t * gpd)
+{
+ return (gpd - gbp_policy_dpo_pool);
+}
+
+static void
+gbp_policy_dpo_lock (dpo_id_t * dpo)
+{
+ gbp_policy_dpo_t *gpd;
+
+ gpd = gbp_policy_dpo_get_from_dpo (dpo);
+ gpd->gpd_locks++;
+}
+
+static void
+gbp_policy_dpo_unlock (dpo_id_t * dpo)
+{
+ gbp_policy_dpo_t *gpd;
+
+ gpd = gbp_policy_dpo_get_from_dpo (dpo);
+ gpd->gpd_locks--;
+
+ if (0 == gpd->gpd_locks)
+ {
+ dpo_reset (&gpd->gpd_dpo);
+ pool_put (gbp_policy_dpo_pool, gpd);
+ }
+}
+
+static u32
+gbp_policy_dpo_get_urpf (const dpo_id_t * dpo)
+{
+ gbp_policy_dpo_t *gpd;
+
+ gpd = gbp_policy_dpo_get_from_dpo (dpo);
+
+ return (gpd->gpd_sw_if_index);
+}
+
+void
+gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
+ gbp_scope_t scope,
+ sclass_t sclass, u32 sw_if_index, dpo_id_t * dpo)
+{
+ gbp_policy_dpo_t *gpd;
+ dpo_id_t parent = DPO_INVALID;
+
+ gpd = gbp_policy_dpo_alloc ();
+
+ gpd->gpd_proto = dproto;
+ gpd->gpd_sw_if_index = sw_if_index;
+ gpd->gpd_sclass = sclass;
+ gpd->gpd_scope = scope;
+
+ if (~0 != sw_if_index)
+ {
+ /*
+ * stack on the DVR DPO for the output interface
+ */
+ dvr_dpo_add_or_lock (sw_if_index, dproto, &parent);
+ }
+ else
+ {
+ dpo_copy (&parent, drop_dpo_get (dproto));
+ }
+
+ dpo_stack (gbp_policy_dpo_type, dproto, &gpd->gpd_dpo, &parent);
+ dpo_set (dpo, gbp_policy_dpo_type, dproto, gbp_policy_dpo_get_index (gpd));
+}
+
+u8 *
+format_gbp_policy_dpo (u8 * s, va_list * ap)
+{
+ index_t index = va_arg (*ap, index_t);
+ u32 indent = va_arg (*ap, u32);
+ gbp_policy_dpo_t *gpd = gbp_policy_dpo_get (index);
+ vnet_main_t *vnm = vnet_get_main ();
+
+ s = format (s, "gbp-policy-dpo: %U, scope:%d sclass:%d out:%U",
+ format_dpo_proto, gpd->gpd_proto,
+ gpd->gpd_scope, (int) gpd->gpd_sclass,
+ format_vnet_sw_if_index_name, vnm, gpd->gpd_sw_if_index);
+ s = format (s, "\n%U", format_white_space, indent + 2);
+ s = format (s, "%U", format_dpo_id, &gpd->gpd_dpo, indent + 4);
+
+ return (s);
+}
+
+/**
+ * Interpose a policy DPO
+ */
+static void
+gbp_policy_dpo_interpose (const dpo_id_t * original,
+ const dpo_id_t * parent, dpo_id_t * clone)
+{
+ gbp_policy_dpo_t *gpd, *gpd_clone;
+
+ gpd_clone = gbp_policy_dpo_alloc ();
+ gpd = gbp_policy_dpo_get (original->dpoi_index);
+
+ gpd_clone->gpd_proto = gpd->gpd_proto;
+ gpd_clone->gpd_scope = gpd->gpd_scope;
+ gpd_clone->gpd_sclass = gpd->gpd_sclass;
+ gpd_clone->gpd_sw_if_index = gpd->gpd_sw_if_index;
+
+ /*
+ * if no interface is provided, grab one from the parent
+ * on which we stack
+ */
+ if (~0 == gpd_clone->gpd_sw_if_index)
+ gpd_clone->gpd_sw_if_index = dpo_get_urpf (parent);
+
+ dpo_stack (gbp_policy_dpo_type,
+ gpd_clone->gpd_proto, &gpd_clone->gpd_dpo, parent);
+
+ dpo_set (clone,
+ gbp_policy_dpo_type,
+ gpd_clone->gpd_proto, gbp_policy_dpo_get_index (gpd_clone));
+}
+
+const static dpo_vft_t gbp_policy_dpo_vft = {
+ .dv_lock = gbp_policy_dpo_lock,
+ .dv_unlock = gbp_policy_dpo_unlock,
+ .dv_format = format_gbp_policy_dpo,
+ .dv_get_urpf = gbp_policy_dpo_get_urpf,
+ .dv_mk_interpose = gbp_policy_dpo_interpose,
+};
+
+/**
+ * @brief The per-protocol VLIB graph nodes that are assigned to a glean
+ * object.
+ *
+ * this means that these graph nodes are ones from which a glean is the
+ * parent object in the DPO-graph.
+ */
+const static char *const gbp_policy_dpo_ip4_nodes[] = {
+ "ip4-gbp-policy-dpo",
+ NULL,
+};
+
+const static char *const gbp_policy_dpo_ip6_nodes[] = {
+ "ip6-gbp-policy-dpo",
+ NULL,
+};
+
+const static char *const *const gbp_policy_dpo_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = gbp_policy_dpo_ip4_nodes,
+ [DPO_PROTO_IP6] = gbp_policy_dpo_ip6_nodes,
+};
+
+dpo_type_t
+gbp_policy_dpo_get_type (void)
+{
+ return (gbp_policy_dpo_type);
+}
+
+static clib_error_t *
+gbp_policy_dpo_module_init (vlib_main_t * vm)
+{
+ gbp_policy_dpo_type = dpo_register_new_type (&gbp_policy_dpo_vft,
+ gbp_policy_dpo_nodes);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_policy_dpo_module_init);
+#endif /* CLIB_MARCH_VARIANT */
+
+typedef enum
+{
+ GBP_POLICY_DROP,
+ GBP_POLICY_N_NEXT,
+} gbp_policy_next_t;
+
+always_inline u32
+gbp_rule_l3_redirect (const gbp_rule_t * gu, vlib_buffer_t * b0, int is_ip6)
+{
+ gbp_policy_node_t pnode;
+ const dpo_id_t *dpo;
+ dpo_proto_t dproto;
+
+ pnode = (is_ip6 ? GBP_POLICY_NODE_IP6 : GBP_POLICY_NODE_IP4);
+ dproto = (is_ip6 ? DPO_PROTO_IP6 : DPO_PROTO_IP4);
+ dpo = &gu->gu_dpo[pnode][dproto];
+
+ /* The flow hash is still valid as this is a IP packet being switched */
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo->dpoi_index;
+
+ return (dpo->dpoi_next_node);
+}
+
+always_inline uword
+gbp_policy_dpo_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame, u8 is_ip6)
+{
+ gbp_main_t *gm = &gbp_main;
+ u32 n_left_from, next_index, *from, *to_next;
+ u32 n_allow_intra, n_allow_a_bit, n_allow_sclass_1;
+
+ from = vlib_frame_vector_args (from_frame);
+ n_left_from = from_frame->n_vectors;
+ n_allow_intra = n_allow_a_bit = n_allow_sclass_1 = 0;
+
+ 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)
+ {
+ gbp_rule_action_t action0 = GBP_RULE_DENY;
+ u32 acl_match = ~0, rule_match = ~0;
+ const gbp_policy_dpo_t *gpd0;
+ gbp_contract_error_t err0;
+ gbp_contract_key_t key0;
+ vlib_buffer_t *b0;
+ gbp_rule_t *rule0;
+ u32 bi0, next0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+ next0 = GBP_POLICY_DROP;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ gpd0 = gbp_policy_dpo_get (vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = gpd0->gpd_dpo.dpoi_index;
+
+ /*
+ * Reflection check; in and out on an ivxlan tunnel
+ */
+ if ((~0 != vxlan_gbp_tunnel_by_sw_if_index (gpd0->gpd_sw_if_index))
+ && (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_R))
+ {
+ goto trace;
+ }
+
+ if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A)
+ {
+ next0 = gpd0->gpd_dpo.dpoi_next_node;
+ key0.as_u64 = ~0;
+ n_allow_a_bit++;
+ goto trace;
+ }
+
+ /* zero out the key to ensure the pad space is clear */
+ key0.as_u64 = 0;
+ key0.gck_src = vnet_buffer2 (b0)->gbp.sclass;
+
+ if (SCLASS_INVALID == key0.gck_src)
+ {
+ /*
+ * the src EPG is not set when the packet arrives on an EPG
+ * uplink interface and we do not need to apply policy
+ */
+ next0 = gpd0->gpd_dpo.dpoi_next_node;
+ goto trace;
+ }
+
+ key0.gck_scope = gpd0->gpd_scope;
+ key0.gck_dst = gpd0->gpd_sclass;
+
+ action0 =
+ gbp_contract_apply (vm, gm, &key0, b0, &rule0, &n_allow_intra,
+ &n_allow_sclass_1, &acl_match, &rule_match,
+ &err0,
+ is_ip6 ? GBP_CONTRACT_APPLY_IP6 :
+ GBP_CONTRACT_APPLY_IP4);
+ switch (action0)
+ {
+ case GBP_RULE_PERMIT:
+ next0 = gpd0->gpd_dpo.dpoi_next_node;
+ vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+ break;
+ case GBP_RULE_REDIRECT:
+ next0 = gbp_rule_l3_redirect (rule0, b0, is_ip6);
+ vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+ break;
+ case GBP_RULE_DENY:
+ next0 = GBP_POLICY_DROP;
+ b0->error = node->errors[err0];
+ break;
+ }
+
+ trace:
+ gbp_policy_trace (vm, node, b0, &key0, action0, acl_match,
+ rule_match);
+
+ 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,
+ GBP_CONTRACT_ERROR_ALLOW_INTRA, n_allow_intra);
+ vlib_node_increment_counter (vm, node->node_index,
+ GBP_CONTRACT_ERROR_ALLOW_A_BIT, n_allow_a_bit);
+ vlib_node_increment_counter (vm, node->node_index,
+ GBP_CONTRACT_ERROR_ALLOW_SCLASS_1,
+ n_allow_sclass_1);
+ return from_frame->n_vectors;
+}
+
+VLIB_NODE_FN (ip4_gbp_policy_dpo_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return (gbp_policy_dpo_inline (vm, node, from_frame, 0));
+}
+
+VLIB_NODE_FN (ip6_gbp_policy_dpo_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return (gbp_policy_dpo_inline (vm, node, from_frame, 1));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_gbp_policy_dpo_node) = {
+ .name = "ip4-gbp-policy-dpo",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_policy_trace,
+
+ .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+ .error_strings = gbp_contract_error_strings,
+
+ .n_next_nodes = GBP_POLICY_N_NEXT,
+ .next_nodes =
+ {
+ [GBP_POLICY_DROP] = "ip4-drop",
+ }
+};
+VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = {
+ .name = "ip6-gbp-policy-dpo",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_policy_trace,
+
+ .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+ .error_strings = gbp_contract_error_strings,
+
+ .n_next_nodes = GBP_POLICY_N_NEXT,
+ .next_nodes =
+ {
+ [GBP_POLICY_DROP] = "ip6-drop",
+ }
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_policy_dpo.h b/extras/deprecated/plugins/gbp/gbp_policy_dpo.h
new file mode 100644
index 00000000000..77ca5d93bd0
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_policy_dpo.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_POLICY_DPO_H__
+#define __GBP_POLICY_DPO_H__
+
+#include <vnet/dpo/dpo.h>
+#include <vnet/dpo/load_balance.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
+
+/**
+ * @brief
+ * The GBP FWD DPO. Used in the L3 path to select the correct EPG uplink
+ * based on the source EPG.
+ */
+typedef struct gbp_policy_dpo_t_
+{
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+ /**
+ * The protocol of packets using this DPO
+ */
+ dpo_proto_t gpd_proto;
+
+ /**
+ * SClass
+ */
+ sclass_t gpd_sclass;
+
+ /**
+ * sclass scope
+ */
+ gbp_scope_t gpd_scope;
+
+ /**
+ * output sw_if_index
+ */
+ u32 gpd_sw_if_index;
+
+ /**
+ * number of locks.
+ */
+ u16 gpd_locks;
+
+ /**
+ * Stacked DPO on DVR/ADJ of output interface
+ */
+ dpo_id_t gpd_dpo;
+} gbp_policy_dpo_t;
+
+extern void gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
+ gbp_scope_t scope,
+ sclass_t sclass,
+ u32 sw_if_index, dpo_id_t * dpo);
+
+extern dpo_type_t gbp_policy_dpo_get_type (void);
+
+extern vlib_node_registration_t ip4_gbp_policy_dpo_node;
+extern vlib_node_registration_t ip6_gbp_policy_dpo_node;
+extern vlib_node_registration_t gbp_policy_port_node;
+
+/**
+ * Types exposed for the Data-plane
+ */
+extern dpo_type_t gbp_policy_dpo_type;
+extern gbp_policy_dpo_t *gbp_policy_dpo_pool;
+
+always_inline gbp_policy_dpo_t *
+gbp_policy_dpo_get (index_t index)
+{
+ return (pool_elt_at_index (gbp_policy_dpo_pool, index));
+}
+
+static_always_inline const gbp_policy_dpo_t *
+gbp_classify_get_gpd (const ip4_address_t * ip4, const ip6_address_t * ip6,
+ const u32 fib_index)
+{
+ const gbp_policy_dpo_t *gpd;
+ const dpo_id_t *dpo;
+ const load_balance_t *lb;
+ u32 lbi;
+
+ if (ip4)
+ lbi = ip4_fib_forwarding_lookup (fib_index, ip4);
+ else if (ip6)
+ lbi = ip6_fib_table_fwding_lookup (fib_index, ip6);
+ else
+ return 0;
+
+ lb = load_balance_get (lbi);
+ dpo = load_balance_get_bucket_i (lb, 0);
+
+ if (dpo->dpoi_type != gbp_policy_dpo_type)
+ return 0;
+
+ gpd = gbp_policy_dpo_get (dpo->dpoi_index);
+ return gpd;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
diff --git a/extras/deprecated/plugins/gbp/gbp_policy_node.c b/extras/deprecated/plugins/gbp/gbp_policy_node.c
new file mode 100644
index 00000000000..8c6ef5c2b94
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_policy_node.c
@@ -0,0 +1,341 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_classify.h>
+#include <plugins/gbp/gbp_policy.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_ext_itf.h>
+#include <plugins/gbp/gbp_contract.h>
+
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+
+typedef enum
+{
+ GBP_POLICY_NEXT_DROP,
+ GBP_POLICY_N_NEXT,
+} gbp_policy_next_t;
+
+always_inline dpo_proto_t
+ethertype_to_dpo_proto (u16 etype)
+{
+ etype = clib_net_to_host_u16 (etype);
+
+ switch (etype)
+ {
+ case ETHERNET_TYPE_IP4:
+ return (DPO_PROTO_IP4);
+ case ETHERNET_TYPE_IP6:
+ return (DPO_PROTO_IP6);
+ }
+
+ return (DPO_PROTO_NONE);
+}
+
+always_inline u32
+gbp_rule_l2_redirect (const gbp_rule_t * gu, vlib_buffer_t * b0)
+{
+ const ethernet_header_t *eth0;
+ const dpo_id_t *dpo;
+ dpo_proto_t dproto;
+
+ eth0 = vlib_buffer_get_current (b0);
+ /* pop the ethernet header to prepare for L3 rewrite */
+ vlib_buffer_advance (b0, vnet_buffer (b0)->l2.l2_len);
+
+ dproto = ethertype_to_dpo_proto (eth0->type);
+ dpo = &gu->gu_dpo[GBP_POLICY_NODE_L2][dproto];
+
+ /* save the LB index for the next node and reset the IP flow hash
+ * so it's recalculated */
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo->dpoi_index;
+ vnet_buffer (b0)->ip.flow_hash = 0;
+
+ return (dpo->dpoi_next_node);
+}
+
+static_always_inline gbp_policy_next_t
+gbp_policy_l2_feature_next (gbp_policy_main_t * gpm, vlib_buffer_t * b,
+ const gbp_policy_type_t type)
+{
+ u32 feat_bit;
+
+ switch (type)
+ {
+ case GBP_POLICY_PORT:
+ feat_bit = L2OUTPUT_FEAT_GBP_POLICY_PORT;
+ break;
+ case GBP_POLICY_MAC:
+ feat_bit = L2OUTPUT_FEAT_GBP_POLICY_MAC;
+ break;
+ case GBP_POLICY_LPM:
+ feat_bit = L2OUTPUT_FEAT_GBP_POLICY_LPM;
+ break;
+ default:
+ return GBP_POLICY_NEXT_DROP;
+ }
+
+ return vnet_l2_feature_next (b, gpm->l2_output_feat_next[type], feat_bit);
+}
+
+static uword
+gbp_policy_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, const gbp_policy_type_t type)
+{
+ gbp_main_t *gm = &gbp_main;
+ gbp_policy_main_t *gpm = &gbp_policy_main;
+ u32 n_left_from, *from, *to_next;
+ u32 next_index;
+ u32 n_allow_intra, n_allow_a_bit, n_allow_sclass_1;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+ n_allow_intra = n_allow_a_bit = n_allow_sclass_1 = 0;
+
+ 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)
+ {
+ gbp_rule_action_t action0 = GBP_RULE_DENY;
+ const ethernet_header_t *h0;
+ const gbp_endpoint_t *ge0;
+ gbp_contract_error_t err0;
+ u32 acl_match = ~0, rule_match = ~0;
+ gbp_policy_next_t next0;
+ gbp_contract_key_t key0;
+ u32 bi0, sw_if_index0;
+ vlib_buffer_t *b0;
+ gbp_rule_t *rule0;
+
+ next0 = GBP_POLICY_NEXT_DROP;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ h0 = vlib_buffer_get_current (b0);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+
+ /*
+ * Reflection check; in and out on an ivxlan tunnel
+ */
+ if ((~0 != vxlan_gbp_tunnel_by_sw_if_index (sw_if_index0)) &&
+ (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_R))
+ {
+ goto trace;
+ }
+
+ /*
+ * If the A-bit is set then policy has already been applied
+ * and we skip enforcement here.
+ */
+ if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A)
+ {
+ next0 = gbp_policy_l2_feature_next (gpm, b0, type);
+ n_allow_a_bit++;
+ key0.as_u64 = ~0;
+ goto trace;
+ }
+
+ /*
+ * determine the src and dst EPG
+ */
+
+ /* zero out the key to ensure the pad space is clear */
+ key0.as_u64 = 0;
+ key0.gck_src = vnet_buffer2 (b0)->gbp.sclass;
+ key0.gck_dst = SCLASS_INVALID;
+
+ if (GBP_POLICY_LPM == type)
+ {
+ const ip4_address_t *ip4 = 0;
+ const ip6_address_t *ip6 = 0;
+ const dpo_proto_t proto =
+ gbp_classify_get_ip_address (h0, &ip4, &ip6,
+ GBP_CLASSIFY_GET_IP_DST);
+ if (PREDICT_TRUE (DPO_PROTO_NONE != proto))
+ {
+ const gbp_ext_itf_t *ext_itf =
+ gbp_ext_itf_get (sw_if_index0);
+ const gbp_policy_dpo_t *gpd =
+ gbp_classify_get_gpd (ip4, ip6,
+ ext_itf->gx_fib_index[proto]);
+ if (gpd)
+ key0.gck_dst = gpd->gpd_sclass;
+ }
+ }
+ else
+ {
+ if (GBP_POLICY_PORT == type)
+ ge0 = gbp_endpoint_find_itf (sw_if_index0);
+ else
+ ge0 = gbp_endpoint_find_mac (h0->dst_address,
+ vnet_buffer (b0)->l2.bd_index);
+ if (NULL != ge0)
+ key0.gck_dst = ge0->ge_fwd.gef_sclass;
+ }
+
+ if (SCLASS_INVALID == key0.gck_dst)
+ {
+ /* If you cannot determine the destination EP then drop */
+ b0->error = node->errors[GBP_CONTRACT_ERROR_DROP_NO_DCLASS];
+ goto trace;
+ }
+
+ key0.gck_src = vnet_buffer2 (b0)->gbp.sclass;
+ if (SCLASS_INVALID == key0.gck_src)
+ {
+ /*
+ * the src EPG is not set when the packet arrives on an EPG
+ * uplink interface and we do not need to apply policy
+ */
+ next0 = gbp_policy_l2_feature_next (gpm, b0, type);
+ goto trace;
+ }
+
+ key0.gck_scope =
+ gbp_bridge_domain_get_scope (vnet_buffer (b0)->l2.bd_index);
+
+ action0 =
+ gbp_contract_apply (vm, gm, &key0, b0, &rule0, &n_allow_intra,
+ &n_allow_sclass_1, &acl_match, &rule_match,
+ &err0, GBP_CONTRACT_APPLY_L2);
+ switch (action0)
+ {
+ case GBP_RULE_PERMIT:
+ next0 = gbp_policy_l2_feature_next (gpm, b0, type);
+ vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+ break;
+ case GBP_RULE_REDIRECT:
+ next0 = gbp_rule_l2_redirect (rule0, b0);
+ vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+ break;
+ case GBP_RULE_DENY:
+ next0 = GBP_POLICY_NEXT_DROP;
+ b0->error = node->errors[err0];
+ break;
+ }
+
+ trace:
+ gbp_policy_trace (vm, node, b0, &key0, action0, acl_match,
+ rule_match);
+
+ /* 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,
+ GBP_CONTRACT_ERROR_ALLOW_INTRA, n_allow_intra);
+ vlib_node_increment_counter (vm, node->node_index,
+ GBP_CONTRACT_ERROR_ALLOW_A_BIT, n_allow_a_bit);
+ vlib_node_increment_counter (vm, node->node_index,
+ GBP_CONTRACT_ERROR_ALLOW_SCLASS_1,
+ n_allow_sclass_1);
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (gbp_policy_port_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_policy_inline (vm, node, frame, GBP_POLICY_PORT));
+}
+
+VLIB_NODE_FN (gbp_policy_mac_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_policy_inline (vm, node, frame, GBP_POLICY_MAC));
+}
+
+VLIB_NODE_FN (gbp_policy_lpm_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (gbp_policy_inline (vm, node, frame, GBP_POLICY_LPM));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_policy_port_node) = {
+ .name = "gbp-policy-port",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_policy_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+ .error_strings = gbp_contract_error_strings,
+
+ .n_next_nodes = GBP_POLICY_N_NEXT,
+ .next_nodes = {
+ [GBP_POLICY_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (gbp_policy_mac_node) = {
+ .name = "gbp-policy-mac",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_policy_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+ .error_strings = gbp_contract_error_strings,
+
+ .n_next_nodes = GBP_POLICY_N_NEXT,
+ .next_nodes = {
+ [GBP_POLICY_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (gbp_policy_lpm_node) = {
+ .name = "gbp-policy-lpm",
+ .vector_size = sizeof (u32),
+ .format_trace = format_gbp_policy_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+ .error_strings = gbp_contract_error_strings,
+
+ .n_next_nodes = GBP_POLICY_N_NEXT,
+ .next_nodes = {
+ [GBP_POLICY_NEXT_DROP] = "error-drop",
+ },
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_recirc.c b/extras/deprecated/plugins/gbp/gbp_recirc.c
new file mode 100644
index 00000000000..8d56f11b4e3
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_recirc.c
@@ -0,0 +1,292 @@
+/*
+ * 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 <plugins/gbp/gbp_recirc.h>
+#include <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_itf.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+
+#include <vlib/unix/plugin.h>
+
+/**
+ * Pool of GBP recircs
+ */
+gbp_recirc_t *gbp_recirc_pool;
+
+/**
+ * Recirc configs keyed by sw_if_index
+ */
+index_t *gbp_recirc_db;
+
+/**
+ * logger
+ */
+vlib_log_class_t gr_logger;
+
+/**
+ * L2 Emulation enable/disable symbols
+ */
+static void (*l2e_enable) (u32 sw_if_index);
+static void (*l2e_disable) (u32 sw_if_index);
+
+#define GBP_RECIRC_DBG(...) \
+ vlib_log_debug (gr_logger, __VA_ARGS__);
+
+u8 *
+format_gbp_recirc (u8 * s, va_list * args)
+{
+ gbp_recirc_t *gr = va_arg (*args, gbp_recirc_t *);
+ vnet_main_t *vnm = vnet_get_main ();
+
+ return format (s, " %U, sclass:%d, ext:%d",
+ format_vnet_sw_if_index_name, vnm,
+ gr->gr_sw_if_index, gr->gr_sclass, gr->gr_is_ext);
+}
+
+int
+gbp_recirc_add (u32 sw_if_index, sclass_t sclass, u8 is_ext)
+{
+ gbp_recirc_t *gr;
+ index_t gri;
+
+ vec_validate_init_empty (gbp_recirc_db, sw_if_index, INDEX_INVALID);
+
+ gri = gbp_recirc_db[sw_if_index];
+
+ if (INDEX_INVALID == gri)
+ {
+ gbp_endpoint_group_t *gg;
+ fib_protocol_t fproto;
+ index_t ggi;
+
+ ggi = gbp_endpoint_group_find (sclass);
+
+ if (INDEX_INVALID == ggi)
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+ gbp_endpoint_group_lock (ggi);
+ pool_get_zero (gbp_recirc_pool, gr);
+ gri = gr - gbp_recirc_pool;
+
+ gr->gr_sclass = sclass;
+ gr->gr_is_ext = is_ext;
+ gr->gr_sw_if_index = sw_if_index;
+
+ /*
+ * IP enable the recirc interface
+ */
+ ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 1);
+ ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 1);
+
+ /*
+ * cache the FIB indicies of the EPG
+ */
+ gr->gr_epgi = ggi;
+
+ gg = gbp_endpoint_group_get (gr->gr_epgi);
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ gr->gr_fib_index[fib_proto_to_dpo (fproto)] =
+ gbp_endpoint_group_get_fib_index (gg, fproto);
+ }
+
+ /*
+ * bind to the bridge-domain of the EPG
+ */
+ gr->gr_itf = gbp_itf_l2_add_and_lock (gr->gr_sw_if_index, gg->gg_gbd);
+
+ /*
+ * set the interface into L2 emulation mode
+ */
+ l2e_enable (gr->gr_sw_if_index);
+
+ /*
+ * Packets on the recirculation interface are subject to src-EPG
+ * classification. Recirc interfaces are L2-emulation mode.
+ * for internal EPGs this is via an LPM on all external subnets.
+ * for external EPGs this is via a port mapping.
+ */
+ if (gr->gr_is_ext)
+ {
+ mac_address_t mac;
+ /*
+ * recirc is for post-NAT translation packets going into
+ * the external EPG, these are classified to the NAT EPG
+ * based on its port
+ */
+ mac_address_from_bytes (&mac,
+ vnet_sw_interface_get_hw_address
+ (vnet_get_main (), gr->gr_sw_if_index));
+ gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP,
+ gr->gr_sw_if_index,
+ NULL, &mac, INDEX_INVALID,
+ INDEX_INVALID, gr->gr_sclass,
+ GBP_ENDPOINT_FLAG_NONE,
+ NULL, NULL, &gr->gr_ep);
+ vnet_feature_enable_disable ("ip4-unicast",
+ "ip4-gbp-src-classify",
+ gr->gr_sw_if_index, 1, 0, 0);
+ vnet_feature_enable_disable ("ip6-unicast",
+ "ip6-gbp-src-classify",
+ gr->gr_sw_if_index, 1, 0, 0);
+ }
+ else
+ {
+ /*
+ * recirc is for pre-NAT translation packets coming from
+ * the external EPG, these are classified based on a LPM
+ * in the EPG's route-domain
+ */
+ vnet_feature_enable_disable ("ip4-unicast",
+ "ip4-gbp-lpm-classify",
+ gr->gr_sw_if_index, 1, 0, 0);
+ vnet_feature_enable_disable ("ip6-unicast",
+ "ip6-gbp-lpm-classify",
+ gr->gr_sw_if_index, 1, 0, 0);
+ }
+
+ gbp_recirc_db[sw_if_index] = gri;
+ }
+ else
+ {
+ gr = gbp_recirc_get (gri);
+ }
+
+ GBP_RECIRC_DBG ("add: %U", format_gbp_recirc, gr);
+ return (0);
+}
+
+int
+gbp_recirc_delete (u32 sw_if_index)
+{
+ gbp_recirc_t *gr;
+ index_t gri;
+
+ if (vec_len (gbp_recirc_db) <= sw_if_index)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ gri = gbp_recirc_db[sw_if_index];
+
+ if (INDEX_INVALID != gri)
+ {
+ gr = pool_elt_at_index (gbp_recirc_pool, gri);
+
+ GBP_RECIRC_DBG ("del: %U", format_gbp_recirc, gr);
+
+ if (gr->gr_is_ext)
+ {
+ gbp_endpoint_unlock (GBP_ENDPOINT_SRC_CP, gr->gr_ep);
+ vnet_feature_enable_disable ("ip4-unicast",
+ "ip4-gbp-src-classify",
+ gr->gr_sw_if_index, 0, 0, 0);
+ vnet_feature_enable_disable ("ip6-unicast",
+ "ip6-gbp-src-classify",
+ gr->gr_sw_if_index, 0, 0, 0);
+ }
+ else
+ {
+ vnet_feature_enable_disable ("ip4-unicast",
+ "ip4-gbp-lpm-classify",
+ gr->gr_sw_if_index, 0, 0, 0);
+ vnet_feature_enable_disable ("ip6-unicast",
+ "ip6-gbp-lpm-classify",
+ gr->gr_sw_if_index, 0, 0, 0);
+ }
+
+ ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
+ ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
+ l2e_disable (gr->gr_sw_if_index);
+
+ gbp_itf_unlock (&gr->gr_itf);
+
+ gbp_endpoint_group_unlock (gr->gr_epgi);
+ gbp_recirc_db[sw_if_index] = INDEX_INVALID;
+ pool_put (gbp_recirc_pool, gr);
+ return (0);
+ }
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
+void
+gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx)
+{
+ gbp_recirc_t *ge;
+
+ /* *INDENT-OFF* */
+ pool_foreach (ge, gbp_recirc_pool)
+ {
+ if (!cb(ge, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static walk_rc_t
+gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx)
+{
+ vlib_cli_output (ctx, " %U", format_gbp_recirc, gr);
+
+ return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+gbp_recirc_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vlib_cli_output (vm, "Recirculation-Interfaces:");
+ gbp_recirc_walk (gbp_recirc_show_one, vm);
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Recircs and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp recirc}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_recirc_show_node, static) = {
+ .path = "show gbp recirc",
+ .short_help = "show gbp recirc\n",
+ .function = gbp_recirc_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_recirc_init (vlib_main_t * vm)
+{
+ gr_logger = vlib_log_register_class ("gbp", "recirc");
+
+ l2e_enable =
+ vlib_get_plugin_symbol ("l2e_plugin.so", "l2_emulation_enable");
+ l2e_disable =
+ vlib_get_plugin_symbol ("l2e_plugin.so", "l2_emulation_disable");
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_recirc_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_recirc.h b/extras/deprecated/plugins/gbp/gbp_recirc.h
new file mode 100644
index 00000000000..2f3354b794e
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_recirc.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_RECIRC_H__
+#define __GBP_RECIRC_H__
+
+#include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
+#include <vnet/fib/fib_types.h>
+
+/**
+ * A GBP recirculation interface representation
+ * Thes interfaces join Bridge domains that are internal to those that are
+ * NAT external, so the packets can be NAT translated and then undergo the
+ * whole policy process again.
+ */
+typedef struct gpb_recirc_t_
+{
+ /**
+ * EPG ID that packets will classify to when they arrive on this recirc
+ */
+ sclass_t gr_sclass;
+
+ /**
+ * The index of the EPG
+ */
+ index_t gr_epgi;
+
+ /**
+ * FIB indices the EPG is mapped to
+ */
+ u32 gr_fib_index[DPO_PROTO_NUM];
+
+ /**
+ * Is the interface for packets post-NAT translation (i.e. ext)
+ * or pre-NAT translation (i.e. internal)
+ */
+ u8 gr_is_ext;
+
+ /**
+ */
+ u32 gr_sw_if_index;
+ gbp_itf_hdl_t gr_itf;
+
+ /**
+ * The endpoint created to represent the reric interface
+ */
+ index_t gr_ep;
+} gbp_recirc_t;
+
+extern int gbp_recirc_add (u32 sw_if_index, sclass_t sclass, u8 is_ext);
+extern int gbp_recirc_delete (u32 sw_if_index);
+
+typedef walk_rc_t (*gbp_recirc_cb_t) (gbp_recirc_t * gbpe, void *ctx);
+extern void gbp_recirc_walk (gbp_recirc_cb_t bgpe, void *ctx);
+
+/**
+ * Data plane functions
+ */
+extern gbp_recirc_t *gbp_recirc_pool;
+extern index_t *gbp_recirc_db;
+
+always_inline gbp_recirc_t *
+gbp_recirc_get (u32 sw_if_index)
+{
+ return (pool_elt_at_index (gbp_recirc_pool, gbp_recirc_db[sw_if_index]));
+}
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_route_domain.c b/extras/deprecated/plugins/gbp/gbp_route_domain.c
new file mode 100644
index 00000000000..6cc595d0fa9
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_route_domain.c
@@ -0,0 +1,447 @@
+/*
+ * 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 <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_endpoint.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+
+/**
+ * A fixed MAC address to use as the source MAC for packets L3 switched
+ * onto the routed uu-fwd interfaces.
+ * Magic values - origin lost to the mists of time...
+ */
+/* *INDENT-OFF* */
+const static mac_address_t GBP_ROUTED_SRC_MAC = {
+ .bytes = {
+ 0x0, 0x22, 0xBD, 0xF8, 0x19, 0xFF,
+ }
+};
+
+const static mac_address_t GBP_ROUTED_DST_MAC = {
+ .bytes = {
+ 00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ }
+};
+/* *INDENT-ON* */
+
+/**
+ * Pool of GBP route_domains
+ */
+gbp_route_domain_t *gbp_route_domain_pool;
+
+/**
+ * DB of route_domains
+ */
+typedef struct gbp_route_domain_db_t
+{
+ uword *gbd_by_rd_id;
+} gbp_route_domain_db_t;
+
+static gbp_route_domain_db_t gbp_route_domain_db;
+static fib_source_t gbp_fib_source;
+
+/**
+ * logger
+ */
+vlib_log_class_t grd_logger;
+
+#define GBP_BD_DBG(...) \
+ vlib_log_debug (grd_logger, __VA_ARGS__);
+
+index_t
+gbp_route_domain_index (const gbp_route_domain_t * grd)
+{
+ return (grd - gbp_route_domain_pool);
+}
+
+gbp_route_domain_t *
+gbp_route_domain_get (index_t i)
+{
+ return (pool_elt_at_index (gbp_route_domain_pool, i));
+}
+
+static void
+gbp_route_domain_lock (index_t i)
+{
+ gbp_route_domain_t *grd;
+
+ grd = gbp_route_domain_get (i);
+ grd->grd_locks++;
+}
+
+index_t
+gbp_route_domain_find (u32 rd_id)
+{
+ uword *p;
+
+ p = hash_get (gbp_route_domain_db.gbd_by_rd_id, rd_id);
+
+ if (NULL != p)
+ return p[0];
+
+ return (INDEX_INVALID);
+}
+
+index_t
+gbp_route_domain_find_and_lock (u32 rd_id)
+{
+ index_t grdi;
+
+ grdi = gbp_route_domain_find (rd_id);
+
+ if (INDEX_INVALID != grdi)
+ {
+ gbp_route_domain_lock (grdi);
+ }
+ return (grdi);
+}
+
+static void
+gbp_route_domain_db_add (gbp_route_domain_t * grd)
+{
+ index_t grdi = grd - gbp_route_domain_pool;
+
+ hash_set (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id, grdi);
+}
+
+static void
+gbp_route_domain_db_remove (gbp_route_domain_t * grd)
+{
+ hash_unset (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id);
+}
+
+int
+gbp_route_domain_add_and_lock (u32 rd_id,
+ gbp_scope_t scope,
+ u32 ip4_table_id,
+ u32 ip6_table_id,
+ u32 ip4_uu_sw_if_index, u32 ip6_uu_sw_if_index)
+{
+ gbp_route_domain_t *grd;
+ index_t grdi;
+
+ grdi = gbp_route_domain_find (rd_id);
+
+ if (INDEX_INVALID == grdi)
+ {
+ fib_protocol_t fproto;
+
+ pool_get_zero (gbp_route_domain_pool, grd);
+
+ grd->grd_id = rd_id;
+ grd->grd_scope = scope;
+ grd->grd_table_id[FIB_PROTOCOL_IP4] = ip4_table_id;
+ grd->grd_table_id[FIB_PROTOCOL_IP6] = ip6_table_id;
+ grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4] = ip4_uu_sw_if_index;
+ grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6] = ip6_uu_sw_if_index;
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ grd->grd_fib_index[fproto] =
+ fib_table_find_or_create_and_lock (fproto,
+ grd->grd_table_id[fproto],
+ gbp_fib_source);
+
+ if (~0 != grd->grd_uu_sw_if_index[fproto])
+ {
+ ethernet_header_t *eth;
+ u8 *rewrite;
+
+ rewrite = NULL;
+ vec_validate (rewrite, sizeof (*eth) - 1);
+ eth = (ethernet_header_t *) rewrite;
+
+ eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
+ ETHERNET_TYPE_IP4 :
+ ETHERNET_TYPE_IP6));
+
+ mac_address_to_bytes (gbp_route_domain_get_local_mac (),
+ eth->src_address);
+ mac_address_to_bytes (gbp_route_domain_get_remote_mac (),
+ eth->dst_address);
+
+ /*
+ * create an adjacency out of the uu-fwd interfaces that will
+ * be used when adding subnet routes.
+ */
+ grd->grd_adj[fproto] =
+ adj_nbr_add_or_lock_w_rewrite (fproto,
+ fib_proto_to_link (fproto),
+ &ADJ_BCAST_ADDR,
+ grd->grd_uu_sw_if_index[fproto],
+ rewrite);
+ }
+ else
+ {
+ grd->grd_adj[fproto] = INDEX_INVALID;
+ }
+ }
+
+ gbp_route_domain_db_add (grd);
+ }
+ else
+ {
+ grd = gbp_route_domain_get (grdi);
+ }
+
+ grd->grd_locks++;
+ GBP_BD_DBG ("add: %U", format_gbp_route_domain, grd);
+
+ return (0);
+}
+
+void
+gbp_route_domain_unlock (index_t index)
+{
+ gbp_route_domain_t *grd;
+
+ grd = gbp_route_domain_get (index);
+
+ grd->grd_locks--;
+
+ if (0 == grd->grd_locks)
+ {
+ fib_protocol_t fproto;
+
+ GBP_BD_DBG ("destroy: %U", format_gbp_route_domain, grd);
+
+ FOR_EACH_FIB_IP_PROTOCOL (fproto)
+ {
+ fib_table_unlock (grd->grd_fib_index[fproto], fproto, gbp_fib_source);
+ if (INDEX_INVALID != grd->grd_adj[fproto])
+ adj_unlock (grd->grd_adj[fproto]);
+ }
+
+ gbp_route_domain_db_remove (grd);
+
+ pool_put (gbp_route_domain_pool, grd);
+ }
+}
+
+u32
+gbp_route_domain_get_rd_id (index_t grdi)
+{
+ gbp_route_domain_t *grd;
+
+ grd = gbp_route_domain_get (grdi);
+
+ return (grd->grd_id);
+}
+
+gbp_scope_t
+gbp_route_domain_get_scope (index_t grdi)
+{
+ gbp_route_domain_t *grd;
+
+ grd = gbp_route_domain_get (grdi);
+
+ return (grd->grd_scope);
+}
+
+int
+gbp_route_domain_delete (u32 rd_id)
+{
+ index_t grdi;
+
+ GBP_BD_DBG ("del: %d", rd_id);
+ grdi = gbp_route_domain_find (rd_id);
+
+ if (INDEX_INVALID != grdi)
+ {
+ GBP_BD_DBG ("del: %U", format_gbp_route_domain,
+ gbp_route_domain_get (grdi));
+ gbp_route_domain_unlock (grdi);
+
+ return (0);
+ }
+
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+const mac_address_t *
+gbp_route_domain_get_local_mac (void)
+{
+ return (&GBP_ROUTED_SRC_MAC);
+}
+
+const mac_address_t *
+gbp_route_domain_get_remote_mac (void)
+{
+ return (&GBP_ROUTED_DST_MAC);
+}
+
+void
+gbp_route_domain_walk (gbp_route_domain_cb_t cb, void *ctx)
+{
+ gbp_route_domain_t *gbpe;
+
+ /* *INDENT-OFF* */
+ pool_foreach (gbpe, gbp_route_domain_pool)
+ {
+ if (!cb(gbpe, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_route_domain_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 ip4_uu_sw_if_index = ~0;
+ u32 ip6_uu_sw_if_index = ~0;
+ u32 ip4_table_id = ~0;
+ u32 ip6_table_id = ~0;
+ u32 scope = ~0;
+ u32 rd_id = ~0;
+ u8 add = 1;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "ip4-uu %U", unformat_vnet_sw_interface,
+ vnm, &ip4_uu_sw_if_index))
+ ;
+ else if (unformat (input, "ip6-uu %U", unformat_vnet_sw_interface,
+ vnm, &ip6_uu_sw_if_index))
+ ;
+ else if (unformat (input, "ip4-table-id %d", &ip4_table_id))
+ ;
+ else if (unformat (input, "ip6-table-id %d", &ip6_table_id))
+ ;
+ else if (unformat (input, "add"))
+ add = 1;
+ else if (unformat (input, "del"))
+ add = 0;
+ else if (unformat (input, "rd %d", &rd_id))
+ ;
+ else if (unformat (input, "scope %d", &scope))
+ ;
+ else
+ break;
+ }
+
+ if (~0 == rd_id)
+ return clib_error_return (0, "RD-ID must be specified");
+
+ if (add)
+ {
+ if (~0 == ip4_table_id)
+ return clib_error_return (0, "IP4 table-ID must be specified");
+ if (~0 == ip6_table_id)
+ return clib_error_return (0, "IP6 table-ID must be specified");
+
+ gbp_route_domain_add_and_lock (rd_id, scope,
+ ip4_table_id,
+ ip6_table_id,
+ ip4_uu_sw_if_index, ip6_uu_sw_if_index);
+ }
+ else
+ gbp_route_domain_delete (rd_id);
+
+ return (NULL);
+}
+
+/*?
+ * Configure a GBP route-domain
+ *
+ * @cliexpar
+ * @cliexstart{gbp route-domain [del] rd <ID> ip4-table-id <ID> ip6-table-id <ID> [ip4-uu <interface>] [ip6-uu <interface>]}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_route_domain_cli_node, static) = {
+ .path = "gbp route-domain",
+ .short_help = "gbp route-domain [del] rd <ID> ip4-table-id <ID> ip6-table-id <ID> [ip4-uu <interface>] [ip6-uu <interface>]",
+ .function = gbp_route_domain_cli,
+};
+
+u8 *
+format_gbp_route_domain (u8 * s, va_list * args)
+{
+ gbp_route_domain_t *grd = va_arg (*args, gbp_route_domain_t*);
+ vnet_main_t *vnm = vnet_get_main ();
+
+ if (NULL != grd)
+ s = format (s, "[%d] rd:%d ip4-uu:%U ip6-uu:%U locks:%d",
+ grd - gbp_route_domain_pool,
+ grd->grd_id,
+ format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4],
+ format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6],
+ grd->grd_locks);
+ else
+ s = format (s, "NULL");
+
+ return (s);
+}
+
+static int
+gbp_route_domain_show_one (gbp_route_domain_t *gb, void *ctx)
+{
+ vlib_main_t *vm;
+
+ vm = ctx;
+ vlib_cli_output (vm, " %U",format_gbp_route_domain, gb);
+
+ return (1);
+}
+
+static clib_error_t *
+gbp_route_domain_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vlib_cli_output (vm, "Route-Domains:");
+ gbp_route_domain_walk (gbp_route_domain_show_one, vm);
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Route_Domains and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp route_domain}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_route_domain_show_node, static) = {
+ .path = "show gbp route-domain",
+ .short_help = "show gbp route-domain\n",
+ .function = gbp_route_domain_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_route_domain_init (vlib_main_t * vm)
+{
+ grd_logger = vlib_log_register_class ("gbp", "rd");
+ gbp_fib_source = fib_source_allocate ("gbp-rd",
+ FIB_SOURCE_PRIORITY_HI,
+ FIB_SOURCE_BH_DROP);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_route_domain_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_route_domain.h b/extras/deprecated/plugins/gbp/gbp_route_domain.h
new file mode 100644
index 00000000000..897c1bdd7ac
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_route_domain.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_ROUTE_DOMAIN_H__
+#define __GBP_ROUTE_DOMAIN_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+#include <vnet/fib/fib_types.h>
+#include <vnet/ethernet/mac_address.h>
+
+/**
+ * A route Domain Representation.
+ * This is a standard route-domain plus all the attributes it must
+ * have to supprt the GBP model.
+ */
+typedef struct gpb_route_domain_t_
+{
+ /**
+ * Route-domain ID
+ */
+ u32 grd_id;
+ gbp_scope_t grd_scope;
+ u32 grd_fib_index[FIB_PROTOCOL_IP_MAX];
+ u32 grd_table_id[FIB_PROTOCOL_IP_MAX];
+
+ /**
+ * The interfaces on which to send packets to unnknown EPs
+ */
+ u32 grd_uu_sw_if_index[FIB_PROTOCOL_IP_MAX];
+
+ /**
+ * adjacencies on the UU interfaces.
+ */
+ u32 grd_adj[FIB_PROTOCOL_IP_MAX];
+
+ u32 grd_locks;
+} gbp_route_domain_t;
+
+extern int gbp_route_domain_add_and_lock (u32 rd_id,
+ gbp_scope_t scope,
+ u32 ip4_table_id,
+ u32 ip6_table_id,
+ u32 ip4_uu_sw_if_index,
+ u32 ip6_uu_sw_if_index);
+extern void gbp_route_domain_unlock (index_t grdi);
+extern index_t gbp_route_domain_find_and_lock (u32 rd_id);
+extern index_t gbp_route_domain_find (u32 rd_id);
+extern index_t gbp_route_domain_index (const gbp_route_domain_t *);
+
+extern int gbp_route_domain_delete (u32 rd_id);
+extern gbp_route_domain_t *gbp_route_domain_get (index_t i);
+extern u32 gbp_route_domain_get_rd_id (index_t i);
+extern gbp_scope_t gbp_route_domain_get_scope (index_t i);
+
+typedef int (*gbp_route_domain_cb_t) (gbp_route_domain_t * gb, void *ctx);
+extern void gbp_route_domain_walk (gbp_route_domain_cb_t bgpe, void *ctx);
+
+extern const mac_address_t *gbp_route_domain_get_local_mac (void);
+extern const mac_address_t *gbp_route_domain_get_remote_mac (void);
+
+extern u8 *format_gbp_route_domain (u8 * s, va_list * args);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_scanner.c b/extras/deprecated/plugins/gbp/gbp_scanner.c
new file mode 100644
index 00000000000..9ae962b7449
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_scanner.c
@@ -0,0 +1,136 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp_scanner.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_vxlan.h>
+
+/**
+ * Scanner logger
+ */
+vlib_log_class_t gs_logger;
+
+/**
+ * Scanner state
+ */
+static bool gs_enabled;
+
+#define GBP_SCANNER_DBG(...) \
+ vlib_log_debug (gs_logger, __VA_ARGS__);
+
+static uword
+gbp_scanner (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ uword event_type, *event_data = 0;
+ bool do_scan = 0;
+
+ while (1)
+ {
+ do_scan = 0;
+
+ if (gs_enabled)
+ {
+ /* scan every 'inactive threshold' seconds */
+ vlib_process_wait_for_event_or_clock (vm, 2);
+ }
+ else
+ vlib_process_wait_for_event (vm);
+
+ event_type = vlib_process_get_events (vm, &event_data);
+ vec_reset_length (event_data);
+
+ switch (event_type)
+ {
+ case ~0:
+ /* timer expired */
+ do_scan = 1;
+ break;
+
+ case GBP_ENDPOINT_SCAN_START:
+ gs_enabled = 1;
+ break;
+
+ case GBP_ENDPOINT_SCAN_STOP:
+ gs_enabled = 0;
+ break;
+
+ case GBP_ENDPOINT_SCAN_SET_TIME:
+ break;
+
+ default:
+ ASSERT (0);
+ }
+
+ if (do_scan)
+ {
+ GBP_SCANNER_DBG ("start");
+ gbp_endpoint_scan (vm);
+ GBP_SCANNER_DBG ("stop");
+ }
+ }
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_scanner_node) = {
+ .function = gbp_scanner,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "gbp-scanner",
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_scanner_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vlib_cli_output (vm, "GBP-scanner: enabled:%d interval:2", gs_enabled);
+
+ return (NULL);
+}
+
+/*?
+ * Show GBP scanner
+ *
+ * @cliexpar
+ * @cliexstart{show gbp scanner}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_scanner_cli_node, static) = {
+ .path = "show gbp scanner",
+ .short_help = "show gbp scanner",
+ .function = gbp_scanner_cli,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_scanner_init (vlib_main_t * vm)
+{
+ gs_logger = vlib_log_register_class ("gbp", "scan");
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_scanner_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/libmemif/test/socket_test.h b/extras/deprecated/plugins/gbp/gbp_scanner.h
index 02ec69cfbd7..1133167d927 100644
--- a/extras/libmemif/test/socket_test.h
+++ b/extras/deprecated/plugins/gbp/gbp_scanner.h
@@ -1,6 +1,5 @@
/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 Cisco and/or its affiliates.
+ * 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:
@@ -12,14 +11,20 @@
* 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 _SOCKET_TEST_H_
-#define _SOCKET_TEST_H_
+#ifndef __GBP_SCANNER_H__
+#define __GBP_SCANNER_H__
-#include <unit_test.h>
+#include <vlib/vlib.h>
-Suite *socket_suite ();
+typedef enum gbp_scan_event_t_
+{
+ GBP_ENDPOINT_SCAN_START,
+ GBP_ENDPOINT_SCAN_STOP,
+ GBP_ENDPOINT_SCAN_SET_TIME,
+} gbp_scan_event_t;
-#endif /* _SOCKET_TEST_H_ */
+extern vlib_node_registration_t gbp_scanner_node;
+
+#endif
diff --git a/extras/deprecated/plugins/gbp/gbp_subnet.c b/extras/deprecated/plugins/gbp/gbp_subnet.c
new file mode 100644
index 00000000000..8d3b571657c
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_subnet.c
@@ -0,0 +1,598 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_fwd_dpo.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_route_domain.h>
+
+#include <vnet/fib/fib_table.h>
+#include <vnet/dpo/load_balance.h>
+
+/**
+ * a key for the DB
+ */
+typedef struct gbp_subnet_key_t_
+{
+ fib_prefix_t gsk_pfx;
+ u32 gsk_fib_index;
+} gbp_subnet_key_t;
+
+/**
+ * Subnet
+ */
+typedef struct gbp_subnet_t_
+{
+ gbp_subnet_key_t *gs_key;
+ gbp_subnet_type_t gs_type;
+ index_t gs_rd;
+
+ union
+ {
+ struct
+ {
+ sclass_t gs_sclass;
+ u32 gs_sw_if_index;
+ } gs_stitched_external;
+ struct
+ {
+ sclass_t gs_sclass;
+ } gs_l3_out;
+ };
+
+ fib_node_index_t gs_fei;
+} gbp_subnet_t;
+
+/**
+ * A DB of the subnets; key={pfx,fib-index}
+ */
+uword *gbp_subnet_db;
+
+/**
+ * pool of subnets
+ */
+gbp_subnet_t *gbp_subnet_pool;
+
+static fib_source_t gbp_fib_source;
+
+static index_t
+gbp_subnet_db_find (u32 fib_index, const fib_prefix_t * pfx)
+{
+ gbp_subnet_key_t key = {
+ .gsk_pfx = *pfx,
+ .gsk_fib_index = fib_index,
+ };
+ uword *p;
+
+ p = hash_get_mem (gbp_subnet_db, &key);
+
+ if (NULL != p)
+ return p[0];
+
+ return (INDEX_INVALID);
+}
+
+static void
+gbp_subnet_db_add (u32 fib_index, const fib_prefix_t * pfx, gbp_subnet_t * gs)
+{
+ gbp_subnet_key_t *key;
+
+ key = clib_mem_alloc (sizeof (*key));
+
+ clib_memcpy (&(key->gsk_pfx), pfx, sizeof (*pfx));
+ key->gsk_fib_index = fib_index;
+
+ hash_set_mem (gbp_subnet_db, key, (gs - gbp_subnet_pool));
+
+ gs->gs_key = key;
+}
+
+static void
+gbp_subnet_db_del (gbp_subnet_t * gs)
+{
+ hash_unset_mem (gbp_subnet_db, gs->gs_key);
+
+ clib_mem_free (gs->gs_key);
+ gs->gs_key = NULL;
+}
+
+
+static int
+gbp_subnet_transport_add (gbp_subnet_t * gs)
+{
+ dpo_id_t gfd = DPO_INVALID;
+ gbp_route_domain_t *grd;
+ fib_protocol_t fproto;
+
+ fproto = gs->gs_key->gsk_pfx.fp_proto;
+ grd = gbp_route_domain_get (gs->gs_rd);
+
+ if (~0 == grd->grd_uu_sw_if_index[fproto])
+ return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
+
+ gs->gs_fei = fib_table_entry_update_one_path (gs->gs_key->gsk_fib_index,
+ &gs->gs_key->gsk_pfx,
+ gbp_fib_source,
+ FIB_ENTRY_FLAG_NONE,
+ fib_proto_to_dpo (fproto),
+ &ADJ_BCAST_ADDR,
+ grd->grd_uu_sw_if_index
+ [fproto], ~0, 1, NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ dpo_reset (&gfd);
+
+ return (0);
+}
+
+static int
+gbp_subnet_internal_add (gbp_subnet_t * gs)
+{
+ dpo_id_t gfd = DPO_INVALID;
+
+ gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
+ &gfd);
+
+ gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index,
+ &gs->gs_key->gsk_pfx,
+ gbp_fib_source,
+ FIB_ENTRY_FLAG_EXCLUSIVE,
+ &gfd);
+
+ dpo_reset (&gfd);
+
+ return (0);
+}
+
+static int
+gbp_subnet_external_add (gbp_subnet_t * gs, u32 sw_if_index, sclass_t sclass)
+{
+ dpo_id_t gpd = DPO_INVALID;
+
+ gs->gs_stitched_external.gs_sclass = sclass;
+ gs->gs_stitched_external.gs_sw_if_index = sw_if_index;
+
+ gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
+ gbp_route_domain_get_scope (gs->gs_rd),
+ gs->gs_stitched_external.gs_sclass,
+ gs->gs_stitched_external.gs_sw_if_index, &gpd);
+
+ gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index,
+ &gs->gs_key->gsk_pfx,
+ gbp_fib_source,
+ (FIB_ENTRY_FLAG_EXCLUSIVE |
+ FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT),
+ &gpd);
+
+ dpo_reset (&gpd);
+
+ return (0);
+}
+
+static int
+gbp_subnet_l3_out_add (gbp_subnet_t * gs, sclass_t sclass, int is_anon)
+{
+ fib_entry_flag_t flags;
+ dpo_id_t gpd = DPO_INVALID;
+
+ gs->gs_l3_out.gs_sclass = sclass;
+
+ gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
+ gbp_route_domain_get_scope (gs->gs_rd),
+ gs->gs_l3_out.gs_sclass, ~0, &gpd);
+
+ flags = FIB_ENTRY_FLAG_INTERPOSE;
+ if (is_anon)
+ flags |= FIB_ENTRY_FLAG_COVERED_INHERIT;
+
+ gs->gs_fei = fib_table_entry_special_dpo_add (gs->gs_key->gsk_fib_index,
+ &gs->gs_key->gsk_pfx,
+ FIB_SOURCE_SPECIAL,
+ flags, &gpd);
+
+ dpo_reset (&gpd);
+
+ return (0);
+}
+
+static void
+gbp_subnet_del_i (index_t gsi)
+{
+ gbp_subnet_t *gs;
+
+ gs = pool_elt_at_index (gbp_subnet_pool, gsi);
+
+ fib_table_entry_delete_index (gs->gs_fei,
+ (GBP_SUBNET_L3_OUT == gs->gs_type
+ || GBP_SUBNET_ANON_L3_OUT ==
+ gs->gs_type) ? FIB_SOURCE_SPECIAL :
+ gbp_fib_source);
+
+ gbp_subnet_db_del (gs);
+ gbp_route_domain_unlock (gs->gs_rd);
+
+ pool_put (gbp_subnet_pool, gs);
+}
+
+int
+gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx)
+{
+ gbp_route_domain_t *grd;
+ index_t gsi, grdi;
+ u32 fib_index;
+
+ grdi = gbp_route_domain_find (rd_id);
+
+ if (~0 == grdi)
+ return (VNET_API_ERROR_NO_SUCH_FIB);
+
+ grd = gbp_route_domain_get (grdi);
+ fib_index = grd->grd_fib_index[pfx->fp_proto];
+
+ gsi = gbp_subnet_db_find (fib_index, pfx);
+
+ if (INDEX_INVALID == gsi)
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+ gbp_subnet_del_i (gsi);
+
+ return (0);
+}
+
+int
+gbp_subnet_add (u32 rd_id,
+ const fib_prefix_t * pfx,
+ gbp_subnet_type_t type, u32 sw_if_index, sclass_t sclass)
+{
+ gbp_route_domain_t *grd;
+ index_t grdi, gsi;
+ gbp_subnet_t *gs;
+ u32 fib_index;
+ int rv;
+
+ switch (type)
+ {
+ case GBP_SUBNET_TRANSPORT:
+ case GBP_SUBNET_STITCHED_INTERNAL:
+ case GBP_SUBNET_STITCHED_EXTERNAL:
+ case GBP_SUBNET_L3_OUT:
+ case GBP_SUBNET_ANON_L3_OUT:
+ break;
+ default:
+ return (VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE);
+ }
+
+ grdi = gbp_route_domain_find_and_lock (rd_id);
+
+ if (~0 == grdi)
+ return (VNET_API_ERROR_NO_SUCH_FIB);
+
+ grd = gbp_route_domain_get (grdi);
+ fib_index = grd->grd_fib_index[pfx->fp_proto];
+
+ gsi = gbp_subnet_db_find (fib_index, pfx);
+
+ /*
+ * this is an update if the subnet already exists, so remove the old
+ */
+ if (INDEX_INVALID != gsi)
+ gbp_subnet_del_i (gsi);
+
+ rv = -2;
+
+ pool_get (gbp_subnet_pool, gs);
+
+ gs->gs_type = type;
+ gs->gs_rd = grdi;
+ gbp_subnet_db_add (fib_index, pfx, gs);
+
+ switch (type)
+ {
+ case GBP_SUBNET_STITCHED_INTERNAL:
+ rv = gbp_subnet_internal_add (gs);
+ break;
+ case GBP_SUBNET_STITCHED_EXTERNAL:
+ rv = gbp_subnet_external_add (gs, sw_if_index, sclass);
+ break;
+ case GBP_SUBNET_TRANSPORT:
+ rv = gbp_subnet_transport_add (gs);
+ break;
+ case GBP_SUBNET_L3_OUT:
+ rv = gbp_subnet_l3_out_add (gs, sclass, 0 /* is_anon */ );
+ break;
+ case GBP_SUBNET_ANON_L3_OUT:
+ rv = gbp_subnet_l3_out_add (gs, sclass, 1 /* is_anon */ );
+ break;
+ }
+
+ return (rv);
+}
+
+static clib_error_t *
+gbp_subnet_add_del_cli (vlib_main_t * vm,
+ 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 ();
+ fib_prefix_t pfx = {.fp_addr = ip46_address_initializer };
+ int length;
+ u32 rd_id = ~0;
+ u32 sw_if_index = ~0;
+ gbp_subnet_type_t type = ~0;
+ u32 sclass = ~0;
+ int is_add = 1;
+ int rv;
+
+ /* 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, "del"))
+ is_add = 0;
+ else if (unformat (line_input, "rd %d", &rd_id))
+ ;
+ else
+ if (unformat
+ (line_input, "prefix %U/%d", unformat_ip4_address,
+ &pfx.fp_addr.ip4, &length))
+ pfx.fp_proto = FIB_PROTOCOL_IP4;
+ else
+ if (unformat
+ (line_input, "prefix %U/%d", unformat_ip6_address,
+ &pfx.fp_addr.ip6, &length))
+ pfx.fp_proto = FIB_PROTOCOL_IP6;
+ else if (unformat (line_input, "type transport"))
+ type = GBP_SUBNET_TRANSPORT;
+ else if (unformat (line_input, "type stitched-internal"))
+ type = GBP_SUBNET_STITCHED_INTERNAL;
+ else if (unformat (line_input, "type stitched-external"))
+ type = GBP_SUBNET_STITCHED_EXTERNAL;
+ else if (unformat (line_input, "type anon-l3-out"))
+ type = GBP_SUBNET_ANON_L3_OUT;
+ else if (unformat (line_input, "type l3-out"))
+ type = GBP_SUBNET_L3_OUT;
+ else
+ if (unformat_user
+ (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
+ ;
+ else if (unformat (line_input, "sclass %u", &sclass))
+ ;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ }
+ unformat_free (line_input);
+
+ pfx.fp_len = length;
+
+ if (is_add)
+ rv = gbp_subnet_add (rd_id, &pfx, type, sw_if_index, sclass);
+ else
+ rv = gbp_subnet_del (rd_id, &pfx);
+
+ switch (rv)
+ {
+ case 0:
+ return 0;
+ case VNET_API_ERROR_NO_SUCH_FIB:
+ return clib_error_return (0, "no such FIB");
+ }
+
+ return clib_error_return (0, "unknown error %d", rv);
+}
+
+/*?
+ * Add Group Based Policy Subnets
+ *
+ * @cliexpar
+ * @cliexstart{gbp subnet [del] rd <ID> prefix <prefix> type <type> [<interface>] [sclass <sclass>]}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_subnet_add_del, static) = {
+ .path = "gbp subnet",
+ .short_help = "gbp subnet [del] rd <ID> prefix <prefix> type <type> [<interface>] [sclass <sclass>]\n",
+ .function = gbp_subnet_add_del_cli,
+};
+/* *INDENT-ON* */
+
+
+
+void
+gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx)
+{
+ gbp_route_domain_t *grd;
+ gbp_subnet_t *gs;
+ u32 sw_if_index;
+ sclass_t sclass;
+
+ sclass = SCLASS_INVALID;
+ sw_if_index = ~0;
+
+ /* *INDENT-OFF* */
+ pool_foreach (gs, gbp_subnet_pool)
+ {
+ grd = gbp_route_domain_get(gs->gs_rd);
+
+ switch (gs->gs_type)
+ {
+ case GBP_SUBNET_STITCHED_INTERNAL:
+ case GBP_SUBNET_TRANSPORT:
+ /* use defaults above */
+ break;
+ case GBP_SUBNET_STITCHED_EXTERNAL:
+ sw_if_index = gs->gs_stitched_external.gs_sw_if_index;
+ sclass = gs->gs_stitched_external.gs_sclass;
+ break;
+ case GBP_SUBNET_L3_OUT:
+ case GBP_SUBNET_ANON_L3_OUT:
+ sclass = gs->gs_l3_out.gs_sclass;
+ break;
+ }
+
+ if (WALK_STOP == cb (grd->grd_id, &gs->gs_key->gsk_pfx,
+ gs->gs_type, sw_if_index, sclass, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+typedef enum gsb_subnet_show_flags_t_
+{
+ GBP_SUBNET_SHOW_BRIEF,
+ GBP_SUBNET_SHOW_DETAILS,
+} gsb_subnet_show_flags_t;
+
+static u8 *
+format_gbp_subnet_type (u8 * s, va_list * args)
+{
+ gbp_subnet_type_t type = va_arg (*args, gbp_subnet_type_t);
+
+ switch (type)
+ {
+ case GBP_SUBNET_STITCHED_INTERNAL:
+ return (format (s, "stitched-internal"));
+ case GBP_SUBNET_STITCHED_EXTERNAL:
+ return (format (s, "stitched-external"));
+ case GBP_SUBNET_TRANSPORT:
+ return (format (s, "transport"));
+ case GBP_SUBNET_L3_OUT:
+ return (format (s, "l3-out"));
+ case GBP_SUBNET_ANON_L3_OUT:
+ return (format (s, "anon-l3-out"));
+ }
+
+ return (format (s, "unknown"));
+}
+
+u8 *
+format_gbp_subnet (u8 * s, va_list * args)
+{
+ index_t gsi = va_arg (*args, index_t);
+ gsb_subnet_show_flags_t flags = va_arg (*args, gsb_subnet_show_flags_t);
+ gbp_subnet_t *gs;
+ u32 table_id;
+
+ gs = pool_elt_at_index (gbp_subnet_pool, gsi);
+
+ table_id = fib_table_get_table_id (gs->gs_key->gsk_fib_index,
+ gs->gs_key->gsk_pfx.fp_proto);
+
+ s = format (s, "[%d] tbl:%d %U %U", gsi, table_id,
+ format_fib_prefix, &gs->gs_key->gsk_pfx,
+ format_gbp_subnet_type, gs->gs_type);
+
+ switch (gs->gs_type)
+ {
+ case GBP_SUBNET_STITCHED_INTERNAL:
+ case GBP_SUBNET_TRANSPORT:
+ break;
+ case GBP_SUBNET_STITCHED_EXTERNAL:
+ s = format (s, " {sclass:%d %U}", gs->gs_stitched_external.gs_sclass,
+ format_vnet_sw_if_index_name,
+ vnet_get_main (), gs->gs_stitched_external.gs_sw_if_index);
+ break;
+ case GBP_SUBNET_L3_OUT:
+ case GBP_SUBNET_ANON_L3_OUT:
+ s = format (s, " {sclass:%d}", gs->gs_l3_out.gs_sclass);
+ break;
+ }
+
+ switch (flags)
+ {
+ case GBP_SUBNET_SHOW_DETAILS:
+ {
+ s = format (s, "\n %U", format_fib_entry, gs->gs_fei,
+ FIB_ENTRY_FORMAT_DETAIL);
+ }
+ case GBP_SUBNET_SHOW_BRIEF:
+ break;
+ }
+ return (s);
+}
+
+static clib_error_t *
+gbp_subnet_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ u32 gsi;
+
+ gsi = INDEX_INVALID;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%d", &gsi))
+ ;
+ else
+ break;
+ }
+
+ if (INDEX_INVALID != gsi)
+ {
+ vlib_cli_output (vm, "%U", format_gbp_subnet, gsi,
+ GBP_SUBNET_SHOW_DETAILS);
+ }
+ else
+ {
+ /* *INDENT-OFF* */
+ pool_foreach_index (gsi, gbp_subnet_pool)
+ {
+ vlib_cli_output (vm, "%U", format_gbp_subnet, gsi,
+ GBP_SUBNET_SHOW_BRIEF);
+ }
+ /* *INDENT-ON* */
+ }
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Subnets
+ *
+ * @cliexpar
+ * @cliexstart{show gbp subnet}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_subnet_show_node, static) = {
+ .path = "show gbp subnet",
+ .short_help = "show gbp subnet\n",
+ .function = gbp_subnet_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_subnet_init (vlib_main_t * vm)
+{
+ gbp_subnet_db = hash_create_mem (0,
+ sizeof (gbp_subnet_key_t), sizeof (u32));
+ gbp_fib_source = fib_source_allocate ("gbp-subnet",
+ FIB_SOURCE_PRIORITY_HI,
+ FIB_SOURCE_BH_SIMPLE);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_subnet_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_subnet.h b/extras/deprecated/plugins/gbp/gbp_subnet.h
new file mode 100644
index 00000000000..6fbef01ceba
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_subnet.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_SUBNET_H__
+#define __GBP_SUBNET_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+typedef enum gbp_subnet_type_t_
+{
+ GBP_SUBNET_TRANSPORT,
+ GBP_SUBNET_STITCHED_INTERNAL,
+ GBP_SUBNET_STITCHED_EXTERNAL,
+ GBP_SUBNET_L3_OUT,
+ GBP_SUBNET_ANON_L3_OUT,
+} gbp_subnet_type_t;
+
+extern int gbp_subnet_add (u32 rd_id,
+ const fib_prefix_t * pfx,
+ gbp_subnet_type_t type,
+ u32 sw_if_index, sclass_t sclass);
+
+extern int gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx);
+
+typedef walk_rc_t (*gbp_subnet_cb_t) (u32 rd_id,
+ const fib_prefix_t * pfx,
+ gbp_subnet_type_t type,
+ u32 sw_if_index,
+ sclass_t sclass, void *ctx);
+
+extern void gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_types.h b/extras/deprecated/plugins/gbp/gbp_types.h
new file mode 100644
index 00000000000..ac983b1cdd2
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_types.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_TYPES_H__
+#define __GBP_TYPES_H__
+
+#include <vnet/vnet.h>
+
+typedef u32 vnid_t;
+#define VNID_INVALID ((u16)~0)
+
+typedef u16 gbp_scope_t;
+typedef u16 sclass_t;
+#define SCLASS_INVALID ((u16)~0)
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_vxlan.c b/extras/deprecated/plugins/gbp/gbp_vxlan.c
new file mode 100644
index 00000000000..77e4d7ac11b
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_vxlan.c
@@ -0,0 +1,654 @@
+/*
+ * 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 <plugins/gbp/gbp_vxlan.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+#include <vlibmemory/api.h>
+#include <vnet/fib/fib_table.h>
+#include <vlib/punt.h>
+
+/**
+ * A reference to a VXLAN-GBP tunnel created as a child/dependent tunnel
+ * of the template GBP-VXLAN tunnel
+ */
+typedef struct vxlan_tunnel_ref_t_
+{
+ gbp_itf_hdl_t vxr_itf;
+ u32 vxr_sw_if_index;
+ index_t vxr_parent;
+ gbp_vxlan_tunnel_layer_t vxr_layer;
+} vxlan_tunnel_ref_t;
+
+/**
+ * DB of added tunnels
+ */
+uword *gv_db;
+
+/**
+ * Logger
+ */
+static vlib_log_class_t gt_logger;
+
+/**
+ * Pool of template tunnels
+ */
+static gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_pool;
+
+/**
+ * Pool of child tunnels
+ */
+static vxlan_tunnel_ref_t *vxlan_tunnel_ref_pool;
+
+/**
+ * DB of template interfaces by SW interface index
+ */
+static index_t *gbp_vxlan_tunnel_db;
+
+/**
+ * DB of child interfaces by SW interface index
+ */
+static index_t *vxlan_tunnel_ref_db;
+
+/**
+ * handle registered with the ;unt infra
+ */
+static vlib_punt_hdl_t punt_hdl;
+
+static char *gbp_vxlan_tunnel_layer_strings[] = {
+#define _(n,s) [GBP_VXLAN_TUN_##n] = s,
+ foreach_gbp_vxlan_tunnel_layer
+#undef _
+};
+
+#define GBP_VXLAN_TUN_DBG(...) \
+ vlib_log_debug (gt_logger, __VA_ARGS__);
+
+
+gbp_vxlan_tunnel_t *
+gbp_vxlan_tunnel_get (index_t gti)
+{
+ return (pool_elt_at_index (gbp_vxlan_tunnel_pool, gti));
+}
+
+static vxlan_tunnel_ref_t *
+vxlan_tunnel_ref_get (index_t vxri)
+{
+ return (pool_elt_at_index (vxlan_tunnel_ref_pool, vxri));
+}
+
+static u8 *
+format_vxlan_tunnel_ref (u8 * s, va_list * args)
+{
+ index_t vxri = va_arg (*args, u32);
+ vxlan_tunnel_ref_t *vxr;
+
+ vxr = vxlan_tunnel_ref_get (vxri);
+
+ s = format (s, "[%U]", format_gbp_itf_hdl, vxr->vxr_itf);
+
+ return (s);
+}
+
+static void
+gdb_vxlan_dep_del (u32 sw_if_index)
+{
+ vxlan_tunnel_ref_t *vxr;
+ gbp_vxlan_tunnel_t *gt;
+ index_t vxri;
+ u32 pos;
+
+ vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]);
+ vxri = vxr - vxlan_tunnel_ref_pool;
+ gt = gbp_vxlan_tunnel_get (vxr->vxr_parent);
+
+ GBP_VXLAN_TUN_DBG ("del-dep:%U", format_vxlan_tunnel_ref, vxri);
+
+ vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = INDEX_INVALID;
+ pos = vec_search (gt->gt_tuns, vxri);
+
+ ASSERT (~0 != pos);
+ vec_del1 (gt->gt_tuns, pos);
+
+ vnet_vxlan_gbp_tunnel_del (vxr->vxr_sw_if_index);
+
+ pool_put (vxlan_tunnel_ref_pool, vxr);
+}
+
+static gbp_itf_hdl_t
+gdb_vxlan_dep_add (gbp_vxlan_tunnel_t * gt,
+ const ip46_address_t * src, const ip46_address_t * dst)
+{
+ vnet_vxlan_gbp_tunnel_add_del_args_t args = {
+ .is_add = 1,
+ .is_ip6 = !ip46_address_is_ip4 (src),
+ .vni = gt->gt_vni,
+ .src = *src,
+ .dst = *dst,
+ .instance = ~0,
+ .mode = (GBP_VXLAN_TUN_L2 == gt->gt_layer ?
+ VXLAN_GBP_TUNNEL_MODE_L2 : VXLAN_GBP_TUNNEL_MODE_L3),
+ };
+ vxlan_tunnel_ref_t *vxr;
+ u32 sw_if_index;
+ index_t vxri;
+ int rv;
+
+ sw_if_index = ~0;
+ rv = vnet_vxlan_gbp_tunnel_add_del (&args, &sw_if_index);
+
+ if (VNET_API_ERROR_TUNNEL_EXIST == rv)
+ {
+ vxri = vxlan_tunnel_ref_db[sw_if_index];
+
+ vxr = vxlan_tunnel_ref_get (vxri);
+ gbp_itf_lock (vxr->vxr_itf);
+ }
+ else if (0 == rv)
+ {
+ ASSERT (~0 != sw_if_index);
+ GBP_VXLAN_TUN_DBG ("add-dep:%U %U %U %d", format_vnet_sw_if_index_name,
+ vnet_get_main (), sw_if_index,
+ format_ip46_address, src, IP46_TYPE_ANY,
+ format_ip46_address, dst, IP46_TYPE_ANY, gt->gt_vni);
+
+ pool_get_zero (vxlan_tunnel_ref_pool, vxr);
+
+ vxri = (vxr - vxlan_tunnel_ref_pool);
+ vxr->vxr_parent = gt - gbp_vxlan_tunnel_pool;
+ vxr->vxr_sw_if_index = sw_if_index;
+ vxr->vxr_layer = gt->gt_layer;
+
+ /*
+ * store the child both on the parent's list and the global DB
+ */
+ vec_add1 (gt->gt_tuns, vxri);
+
+ vec_validate_init_empty (vxlan_tunnel_ref_db,
+ vxr->vxr_sw_if_index, INDEX_INVALID);
+ vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = vxri;
+
+ if (GBP_VXLAN_TUN_L2 == vxr->vxr_layer)
+ {
+ l2output_feat_masks_t ofeat;
+ l2input_feat_masks_t ifeat;
+ gbp_bridge_domain_t *gbd;
+
+ gbd = gbp_bridge_domain_get (gt->gt_gbd);
+ vxr->vxr_itf = gbp_itf_l2_add_and_lock_w_free
+ (vxr->vxr_sw_if_index, gt->gt_gbd, gdb_vxlan_dep_del);
+
+ ofeat = L2OUTPUT_FEAT_GBP_POLICY_MAC;
+ ifeat = L2INPUT_FEAT_NONE;
+
+ if (!(gbd->gb_flags & GBP_BD_FLAG_DO_NOT_LEARN))
+ ifeat |= L2INPUT_FEAT_GBP_LEARN;
+
+ gbp_itf_l2_set_output_feature (vxr->vxr_itf, ofeat);
+ gbp_itf_l2_set_input_feature (vxr->vxr_itf, ifeat);
+ }
+ else
+ {
+ vxr->vxr_itf = gbp_itf_l3_add_and_lock_w_free
+ (vxr->vxr_sw_if_index, gt->gt_grd, gdb_vxlan_dep_del);
+
+ gbp_itf_l3_set_input_feature (vxr->vxr_itf, GBP_ITF_L3_FEAT_LEARN);
+ }
+ }
+ else
+ {
+ return (GBP_ITF_HDL_INVALID);
+ }
+
+ return (vxr->vxr_itf);
+}
+
+u32
+vxlan_gbp_tunnel_get_parent (u32 sw_if_index)
+{
+ ASSERT ((sw_if_index < vec_len (vxlan_tunnel_ref_db)) &&
+ (INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index]));
+
+ gbp_vxlan_tunnel_t *gt;
+ vxlan_tunnel_ref_t *vxr;
+
+ vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]);
+ gt = gbp_vxlan_tunnel_get (vxr->vxr_parent);
+
+ return (gt->gt_sw_if_index);
+}
+
+gbp_itf_hdl_t
+vxlan_gbp_tunnel_lock_itf (u32 sw_if_index)
+{
+ ASSERT ((sw_if_index < vec_len (vxlan_tunnel_ref_db)) &&
+ (INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index]));
+
+ vxlan_tunnel_ref_t *vxr;
+
+ vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]);
+
+ gbp_itf_lock (vxr->vxr_itf);
+
+ return (vxr->vxr_itf);
+}
+
+
+gbp_vxlan_tunnel_type_t
+gbp_vxlan_tunnel_get_type (u32 sw_if_index)
+{
+ if (sw_if_index < vec_len (vxlan_tunnel_ref_db) &&
+ INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index])
+ {
+ return (VXLAN_GBP_TUNNEL);
+ }
+ else if (sw_if_index < vec_len (gbp_vxlan_tunnel_db) &&
+ INDEX_INVALID != gbp_vxlan_tunnel_db[sw_if_index])
+ {
+ return (GBP_VXLAN_TEMPLATE_TUNNEL);
+ }
+
+ ASSERT (0);
+ return (GBP_VXLAN_TEMPLATE_TUNNEL);
+}
+
+gbp_itf_hdl_t
+gbp_vxlan_tunnel_clone_and_lock (u32 sw_if_index,
+ const ip46_address_t * src,
+ const ip46_address_t * dst)
+{
+ gbp_vxlan_tunnel_t *gt;
+ index_t gti;
+
+ gti = gbp_vxlan_tunnel_db[sw_if_index];
+
+ if (INDEX_INVALID == gti)
+ return (GBP_ITF_HDL_INVALID);
+
+ gt = pool_elt_at_index (gbp_vxlan_tunnel_pool, gti);
+
+ return (gdb_vxlan_dep_add (gt, src, dst));
+}
+
+void
+vxlan_gbp_tunnel_unlock (u32 sw_if_index)
+{
+ /* vxlan_tunnel_ref_t *vxr; */
+ /* index_t vxri; */
+
+ /* vxri = vxlan_tunnel_ref_db[sw_if_index]; */
+
+ /* ASSERT (vxri != INDEX_INVALID); */
+
+ /* vxr = vxlan_tunnel_ref_get (vxri); */
+
+ /* gdb_vxlan_dep_del (vxri); */
+}
+
+void
+gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx)
+{
+ gbp_vxlan_tunnel_t *gt;
+
+ /* *INDENT-OFF* */
+ pool_foreach (gt, gbp_vxlan_tunnel_pool)
+ {
+ if (WALK_CONTINUE != cb(gt, ctx))
+ break;
+ }
+ /* *INDENT-ON* */
+}
+
+static walk_rc_t
+gbp_vxlan_tunnel_show_one (gbp_vxlan_tunnel_t * gt, void *ctx)
+{
+ vlib_cli_output (ctx, "%U", format_gbp_vxlan_tunnel,
+ gt - gbp_vxlan_tunnel_pool);
+
+ return (WALK_CONTINUE);
+}
+
+static u8 *
+format_gbp_vxlan_tunnel_name (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+
+ return format (s, "gbp-vxlan-%d", dev_instance);
+}
+
+u8 *
+format_gbp_vxlan_tunnel_layer (u8 * s, va_list * args)
+{
+ gbp_vxlan_tunnel_layer_t gl = va_arg (*args, gbp_vxlan_tunnel_layer_t);
+ s = format (s, "%s", gbp_vxlan_tunnel_layer_strings[gl]);
+
+ return (s);
+}
+
+u8 *
+format_gbp_vxlan_tunnel (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ CLIB_UNUSED (int verbose) = va_arg (*args, int);
+ gbp_vxlan_tunnel_t *gt = gbp_vxlan_tunnel_get (dev_instance);
+ index_t *vxri;
+
+ s = format (s, " [%d] gbp-vxlan-tunnel: hw:%d sw:%d vni:%d %U",
+ dev_instance, gt->gt_hw_if_index,
+ gt->gt_sw_if_index, gt->gt_vni,
+ format_gbp_vxlan_tunnel_layer, gt->gt_layer);
+ if (GBP_VXLAN_TUN_L2 == gt->gt_layer)
+ s = format (s, " BD:%d gbd-index:%d", gt->gt_bd_rd_id, gt->gt_gbd);
+ else
+ s = format (s, " RD:%d grd-index:%d", gt->gt_bd_rd_id, gt->gt_grd);
+
+ s = format (s, " dependents:");
+ vec_foreach (vxri, gt->gt_tuns)
+ {
+ s = format (s, "\n %U, ", format_vxlan_tunnel_ref, *vxri);
+ }
+
+ return s;
+}
+
+typedef struct gbp_vxlan_tx_trace_t_
+{
+ u32 vni;
+} gbp_vxlan_tx_trace_t;
+
+u8 *
+format_gbp_vxlan_tx_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 *);
+ gbp_vxlan_tx_trace_t *t = va_arg (*args, gbp_vxlan_tx_trace_t *);
+
+ s = format (s, "GBP-VXLAN: vni:%d", t->vni);
+
+ return (s);
+}
+
+clib_error_t *
+gbp_vxlan_interface_admin_up_down (vnet_main_t * vnm,
+ u32 hw_if_index, u32 flags)
+{
+ vnet_hw_interface_t *hi;
+ u32 ti;
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+ if (NULL == gbp_vxlan_tunnel_db ||
+ hi->sw_if_index >= vec_len (gbp_vxlan_tunnel_db))
+ return (NULL);
+
+ ti = gbp_vxlan_tunnel_db[hi->sw_if_index];
+
+ if (~0 == ti)
+ /* not one of ours */
+ return (NULL);
+
+ if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+ vnet_hw_interface_set_flags (vnm, hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+ else
+ vnet_hw_interface_set_flags (vnm, hw_if_index, 0);
+
+ return (NULL);
+}
+
+static uword
+gbp_vxlan_interface_tx (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ clib_warning ("you shouldn't be here, leaking buffers...");
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (gbp_vxlan_device_class) = {
+ .name = "GBP VXLAN tunnel-template",
+ .format_device_name = format_gbp_vxlan_tunnel_name,
+ .format_device = format_gbp_vxlan_tunnel,
+ .format_tx_trace = format_gbp_vxlan_tx_trace,
+ .admin_up_down_function = gbp_vxlan_interface_admin_up_down,
+ .tx_function = gbp_vxlan_interface_tx,
+};
+
+VNET_HW_INTERFACE_CLASS (gbp_vxlan_hw_interface_class) = {
+ .name = "GBP-VXLAN",
+ .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+};
+/* *INDENT-ON* */
+
+int
+gbp_vxlan_tunnel_add (u32 vni, gbp_vxlan_tunnel_layer_t layer,
+ u32 bd_rd_id,
+ const ip4_address_t * src, u32 * sw_if_indexp)
+{
+ gbp_vxlan_tunnel_t *gt;
+ index_t gti;
+ uword *p;
+ int rv;
+
+ rv = 0;
+ p = hash_get (gv_db, vni);
+
+ GBP_VXLAN_TUN_DBG ("add: %d %d %d", vni, layer, bd_rd_id);
+
+ if (NULL == p)
+ {
+ vnet_sw_interface_t *si;
+ vnet_hw_interface_t *hi;
+ index_t gbi, grdi;
+ vnet_main_t *vnm;
+
+ gbi = grdi = INDEX_INVALID;
+
+ if (layer == GBP_VXLAN_TUN_L2)
+ {
+ gbi = gbp_bridge_domain_find_and_lock (bd_rd_id);
+
+ if (INDEX_INVALID == gbi)
+ {
+ return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
+ }
+ }
+ else
+ {
+ grdi = gbp_route_domain_find_and_lock (bd_rd_id);
+
+ if (INDEX_INVALID == grdi)
+ {
+ return (VNET_API_ERROR_NO_SUCH_FIB);
+ }
+ }
+
+ vnm = vnet_get_main ();
+ pool_get (gbp_vxlan_tunnel_pool, gt);
+ gti = gt - gbp_vxlan_tunnel_pool;
+
+ gt->gt_vni = vni;
+ gt->gt_layer = layer;
+ gt->gt_bd_rd_id = bd_rd_id;
+ gt->gt_src.ip4.as_u32 = src->as_u32;
+ gt->gt_hw_if_index = vnet_register_interface (vnm,
+ gbp_vxlan_device_class.index,
+ gti,
+ gbp_vxlan_hw_interface_class.index,
+ gti);
+
+ hi = vnet_get_hw_interface (vnm, gt->gt_hw_if_index);
+
+ gt->gt_sw_if_index = hi->sw_if_index;
+
+ /* don't flood packets in a BD to these interfaces */
+ si = vnet_get_sw_interface (vnm, gt->gt_sw_if_index);
+ si->flood_class = VNET_FLOOD_CLASS_NO_FLOOD;
+
+ if (layer == GBP_VXLAN_TUN_L2)
+ {
+ gbp_bridge_domain_t *gb;
+
+ gb = gbp_bridge_domain_get (gbi);
+
+ gt->gt_gbd = gbi;
+ gb->gb_vni = gti;
+ /* set it up as a GBP interface */
+ gt->gt_itf = gbp_itf_l2_add_and_lock (gt->gt_sw_if_index,
+ gt->gt_gbd);
+ gbp_itf_l2_set_input_feature (gt->gt_itf, L2INPUT_FEAT_GBP_LEARN);
+ }
+ else
+ {
+ gt->gt_grd = grdi;
+ gt->gt_itf = gbp_itf_l3_add_and_lock (gt->gt_sw_if_index,
+ gt->gt_grd);
+ gbp_itf_l3_set_input_feature (gt->gt_itf, GBP_ITF_L3_FEAT_LEARN);
+ }
+
+ /*
+ * save the tunnel by VNI and by sw_if_index
+ */
+ hash_set (gv_db, vni, gti);
+
+ vec_validate_init_empty (gbp_vxlan_tunnel_db,
+ gt->gt_sw_if_index, INDEX_INVALID);
+ gbp_vxlan_tunnel_db[gt->gt_sw_if_index] = gti;
+
+ if (sw_if_indexp)
+ *sw_if_indexp = gt->gt_sw_if_index;
+
+ vxlan_gbp_register_udp_ports ();
+ }
+ else
+ {
+ gti = p[0];
+ rv = VNET_API_ERROR_IF_ALREADY_EXISTS;
+ }
+
+ GBP_VXLAN_TUN_DBG ("add: %U", format_gbp_vxlan_tunnel, gti);
+
+ return (rv);
+}
+
+int
+gbp_vxlan_tunnel_del (u32 vni)
+{
+ gbp_vxlan_tunnel_t *gt;
+ uword *p;
+
+ p = hash_get (gv_db, vni);
+
+ if (NULL != p)
+ {
+ vnet_main_t *vnm;
+
+ vnm = vnet_get_main ();
+ gt = gbp_vxlan_tunnel_get (p[0]);
+
+ vxlan_gbp_unregister_udp_ports ();
+
+ GBP_VXLAN_TUN_DBG ("del: %U", format_gbp_vxlan_tunnel,
+ gt - gbp_vxlan_tunnel_pool);
+
+ gbp_endpoint_flush (GBP_ENDPOINT_SRC_DP, gt->gt_sw_if_index);
+ ASSERT (0 == vec_len (gt->gt_tuns));
+ vec_free (gt->gt_tuns);
+
+ gbp_itf_unlock (&gt->gt_itf);
+
+ if (GBP_VXLAN_TUN_L2 == gt->gt_layer)
+ {
+ gbp_bridge_domain_unlock (gt->gt_gbd);
+ }
+ else
+ {
+ gbp_route_domain_unlock (gt->gt_grd);
+ }
+
+ vnet_sw_interface_set_flags (vnm, gt->gt_sw_if_index, 0);
+ vnet_delete_hw_interface (vnm, gt->gt_hw_if_index);
+
+ hash_unset (gv_db, vni);
+ gbp_vxlan_tunnel_db[gt->gt_sw_if_index] = INDEX_INVALID;
+
+ pool_put (gbp_vxlan_tunnel_pool, gt);
+ }
+ else
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ return (0);
+}
+
+static clib_error_t *
+gbp_vxlan_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+
+ vlib_cli_output (vm, "GBP-VXLAN Interfaces:");
+
+ gbp_vxlan_walk (gbp_vxlan_tunnel_show_one, vm);
+
+ return (NULL);
+}
+
+/*?
+ * Show Group Based Policy VXLAN tunnels
+ *
+ * @cliexpar
+ * @cliexstart{show gbp vxlan}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_vxlan_show_node, static) = {
+ .path = "show gbp vxlan",
+ .short_help = "show gbp vxlan\n",
+ .function = gbp_vxlan_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_vxlan_init (vlib_main_t * vm)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+
+ gt_logger = vlib_log_register_class ("gbp", "tun");
+
+ punt_hdl = vlib_punt_client_register ("gbp-vxlan");
+
+ vlib_punt_register (punt_hdl,
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4],
+ "gbp-vxlan4");
+
+ return (0);
+}
+
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (gbp_vxlan_init) =
+{
+ .runs_after = VLIB_INITS("punt_init", "vxlan_gbp_init"),
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_vxlan.h b/extras/deprecated/plugins/gbp/gbp_vxlan.h
new file mode 100644
index 00000000000..706fe2a0e85
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_vxlan.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#ifndef __GBP_VXLAN_H__
+#define __GBP_VXLAN_H__
+
+#include <vnet/fib/fib_types.h>
+#include <plugins/gbp/gbp_itf.h>
+
+#define foreach_gbp_vxlan_tunnel_layer \
+ _ (L2, "l2") \
+ _ (L3, "l3")
+
+typedef enum gbp_vxlan_tunnel_layer_t_
+{
+#define _(s,n) GBP_VXLAN_TUN_##s,
+ foreach_gbp_vxlan_tunnel_layer
+#undef _
+} gbp_vxlan_tunnel_layer_t;
+
+/**
+ * GBP VXLAN (template) tunnel.
+ * A template tunnel has only a VNI, it does not have src,dst address.
+ * As such it cannot be used to send traffic. It is used in the RX path
+ * to RX vxlan-gbp packets that do not match an existing tunnel;
+ */
+typedef struct gbp_vxlan_tunnel_t_
+{
+ u32 gt_hw_if_index;
+ u32 gt_sw_if_index;
+ u32 gt_vni;
+
+ /**
+ * The BD or RD value (depending on the layer) that the tunnel is bound to
+ */
+ u32 gt_bd_rd_id;
+ gbp_vxlan_tunnel_layer_t gt_layer;
+
+ union
+ {
+ struct
+ {
+ /**
+ * Reference to the GPB-BD
+ */
+ index_t gt_gbd;
+ };
+ struct
+ {
+ /**
+ * References to the GBP-RD
+ */
+ index_t gt_grd;
+ };
+ };
+
+ /**
+ * gbp-itf config for this interface
+ */
+ gbp_itf_hdl_t gt_itf;
+
+ /**
+ * list of child vxlan-gbp tunnels built from this template
+ */
+ index_t *gt_tuns;
+
+ /**
+ * The source address to use for child tunnels
+ */
+ ip46_address_t gt_src;
+} gbp_vxlan_tunnel_t;
+
+/**
+ * The different types of interfaces that endpoints are learned on
+ */
+typedef enum gbp_vxlan_tunnel_type_t_
+{
+ /**
+ * This is the object type defined above.
+ * A template representation of a vxlan-gbp tunnel. from this tunnel
+ * type, real vxlan-gbp tunnels are created (by cloning the VNI)
+ */
+ GBP_VXLAN_TEMPLATE_TUNNEL,
+
+ /**
+ * A real VXLAN-GBP tunnel (from vnet/vxlan-gbp/...)
+ */
+ VXLAN_GBP_TUNNEL,
+} gbp_vxlan_tunnel_type_t;
+
+extern int gbp_vxlan_tunnel_add (u32 vni, gbp_vxlan_tunnel_layer_t layer,
+ u32 bd_rd_id,
+ const ip4_address_t * src,
+ u32 * sw_if_indexp);
+extern int gbp_vxlan_tunnel_del (u32 vni);
+
+extern gbp_vxlan_tunnel_type_t gbp_vxlan_tunnel_get_type (u32 sw_if_index);
+
+extern gbp_itf_hdl_t gbp_vxlan_tunnel_clone_and_lock (u32 parent_tunnel,
+ const ip46_address_t *
+ src,
+ const ip46_address_t *
+ dst);
+
+extern u32 vxlan_gbp_tunnel_get_parent (u32 sw_if_index);
+extern gbp_itf_hdl_t vxlan_gbp_tunnel_lock_itf (u32 sw_if_index);
+
+typedef walk_rc_t (*gbp_vxlan_cb_t) (gbp_vxlan_tunnel_t * gt, void *ctx);
+extern void gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx);
+
+extern u8 *format_gbp_vxlan_tunnel (u8 * s, va_list * args);
+extern u8 *format_gbp_vxlan_tunnel_layer (u8 * s, va_list * args);
+
+extern gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_get (index_t gti);
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/gbp_vxlan_node.c b/extras/deprecated/plugins/gbp/gbp_vxlan_node.c
new file mode 100644
index 00000000000..413a9f47e1b
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/gbp_vxlan_node.c
@@ -0,0 +1,218 @@
+/*
+ * 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 <plugins/gbp/gbp_vxlan.h>
+#include <plugins/gbp/gbp_itf.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+#include <vlibmemory/api.h>
+#include <vnet/fib/fib_table.h>
+
+extern uword *gv_db;
+
+typedef struct gbp_vxlan_trace_t_
+{
+ u8 dropped;
+ u32 vni;
+ u32 sw_if_index;
+ u16 sclass;
+ u8 flags;
+} gbp_vxlan_trace_t;
+
+#define foreach_gbp_vxlan_input_next \
+ _(DROP, "error-drop") \
+ _(L2_INPUT, "l2-input") \
+ _(IP4_INPUT, "ip4-input") \
+ _(IP6_INPUT, "ip6-input")
+
+typedef enum
+{
+#define _(s,n) GBP_VXLAN_INPUT_NEXT_##s,
+ foreach_gbp_vxlan_input_next
+#undef _
+ GBP_VXLAN_INPUT_N_NEXT,
+} gbp_vxlan_input_next_t;
+
+
+#define foreach_gbp_vxlan_error \
+ _(DECAPPED, "decapped") \
+ _(LEARNED, "learned")
+
+typedef enum
+{
+#define _(s,n) GBP_VXLAN_ERROR_##s,
+ foreach_gbp_vxlan_error
+#undef _
+ GBP_VXLAN_N_ERROR,
+} gbp_vxlan_input_error_t;
+
+static char *gbp_vxlan_error_strings[] = {
+#define _(n,s) s,
+ foreach_gbp_vxlan_error
+#undef _
+};
+
+static uword
+gbp_vxlan_decap (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame, u8 is_ip4)
+{
+ u32 n_left_to_next, n_left_from, next_index, *to_next, *from;
+
+ next_index = 0;
+ from = vlib_frame_vector_args (from_frame);
+ n_left_from = from_frame->n_vectors;
+
+ while (n_left_from > 0)
+ {
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vxlan_gbp_header_t *vxlan_gbp0;
+ gbp_vxlan_input_next_t next0;
+ gbp_vxlan_tunnel_t *gt0;
+ vlib_buffer_t *b0;
+ u32 bi0, vni0;
+ uword *p;
+
+ bi0 = to_next[0] = from[0];
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+ next0 = GBP_VXLAN_INPUT_NEXT_DROP;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ vxlan_gbp0 =
+ vlib_buffer_get_current (b0) - sizeof (vxlan_gbp_header_t);
+
+ vni0 = vxlan_gbp_get_vni (vxlan_gbp0);
+ p = hash_get (gv_db, vni0);
+
+ if (PREDICT_FALSE (NULL == p))
+ {
+ gt0 = NULL;
+ next0 = GBP_VXLAN_INPUT_NEXT_DROP;
+ }
+ else
+ {
+ gt0 = gbp_vxlan_tunnel_get (p[0]);
+
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = gt0->gt_sw_if_index;
+
+ if (GBP_VXLAN_TUN_L2 == gt0->gt_layer)
+ /*
+ * An L2 layer tunnel goes into the BD
+ */
+ next0 = GBP_VXLAN_INPUT_NEXT_L2_INPUT;
+ else
+ {
+ /*
+ * An L3 layer tunnel needs to strip the L2 header
+ * an inject into the RD
+ */
+ ethernet_header_t *e0;
+ u16 type0;
+
+ e0 = vlib_buffer_get_current (b0);
+ type0 = clib_net_to_host_u16 (e0->type);
+ switch (type0)
+ {
+ case ETHERNET_TYPE_IP4:
+ next0 = GBP_VXLAN_INPUT_NEXT_IP4_INPUT;
+ break;
+ case ETHERNET_TYPE_IP6:
+ next0 = GBP_VXLAN_INPUT_NEXT_IP6_INPUT;
+ break;
+ default:
+ goto trace;
+ }
+ vlib_buffer_advance (b0, sizeof (*e0));
+ }
+ }
+
+ trace:
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ gbp_vxlan_trace_t *tr;
+
+ tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->dropped = (next0 == GBP_VXLAN_INPUT_NEXT_DROP);
+ tr->vni = vni0;
+ tr->sw_if_index = (gt0 ? gt0->gt_sw_if_index : ~0);
+ tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+ tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+ }
+
+ 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);
+ }
+
+ return from_frame->n_vectors;
+}
+
+VLIB_NODE_FN (gbp_vxlan4_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return gbp_vxlan_decap (vm, node, from_frame, 1);
+}
+
+static u8 *
+format_gbp_vxlan_rx_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 *);
+ gbp_vxlan_trace_t *t = va_arg (*args, gbp_vxlan_trace_t *);
+
+ s = format (s, "vni:%d dropped:%d rx:%d sclass:%d flags:%U",
+ t->vni, t->dropped, t->sw_if_index,
+ t->sclass, format_vxlan_gbp_header_gpflags, t->flags);
+
+ return (s);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_vxlan4_input_node) =
+{
+ .name = "gbp-vxlan4",
+ .vector_size = sizeof (u32),
+ .n_errors = GBP_VXLAN_N_ERROR,
+ .error_strings = gbp_vxlan_error_strings,
+ .n_next_nodes = GBP_VXLAN_INPUT_N_NEXT,
+ .format_trace = format_gbp_vxlan_rx_trace,
+ .next_nodes = {
+#define _(s,n) [GBP_VXLAN_INPUT_NEXT_##s] = n,
+ foreach_gbp_vxlan_input_next
+#undef _
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/gbp/test_gbp.py b/extras/deprecated/plugins/gbp/test_gbp.py
new file mode 100644
index 00000000000..6a4fe0dae42
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/test_gbp.py
@@ -0,0 +1,7209 @@
+#!/usr/bin/env python3
+import typing
+from socket import AF_INET6, inet_pton, inet_ntop
+import unittest
+from ipaddress import ip_address, IPv4Network, IPv6Network
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether, ARP, Dot1Q
+from scapy.layers.inet import IP, UDP, ICMP
+from scapy.layers.inet6 import (
+ IPv6,
+ ICMPv6ND_NS,
+ ICMPv6NDOptSrcLLAddr,
+ ICMPv6ND_NA,
+ ICMPv6EchoRequest,
+)
+from scapy.utils6 import in6_getnsma, in6_getnsmac
+from scapy.layers.vxlan import VXLAN
+from scapy.data import ETH_P_IP, ETH_P_IPV6
+
+from framework import tag_fixme_vpp_workers
+from framework import VppTestCase, VppTestRunner
+from vpp_object import VppObject
+from vpp_interface import VppInterface
+from vpp_ip_route import (
+ VppIpRoute,
+ VppRoutePath,
+ VppIpTable,
+ VppIpInterfaceAddress,
+ VppIpInterfaceBind,
+ find_route,
+ FibPathProto,
+ FibPathType,
+)
+from vpp_l2 import (
+ VppBridgeDomain,
+ VppBridgeDomainPort,
+ VppBridgeDomainArpEntry,
+ VppL2FibEntry,
+ find_bridge_domain_port,
+ VppL2Vtr,
+)
+from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
+from vpp_ip import DpoProto, get_dpo_proto
+from vpp_papi import VppEnum, MACAddress
+from vpp_vxlan_gbp_tunnel import find_vxlan_gbp_tunnel, INDEX_INVALID, VppVxlanGbpTunnel
+from vpp_neighbor import VppNeighbor
+from vpp_acl import AclRule, VppAcl
+
+NUM_PKTS = 67
+
+
+def find_gbp_endpoint(
+ test, sw_if_index=None, ip=None, mac=None, tep=None, sclass=None, flags=None
+):
+ if ip:
+ vip = ip
+ if mac:
+ vmac = MACAddress(mac)
+
+ eps = test.vapi.gbp_endpoint_dump()
+
+ for ep in eps:
+ if tep:
+ src = tep[0]
+ dst = tep[1]
+ if src != str(ep.endpoint.tun.src) or dst != str(ep.endpoint.tun.dst):
+ continue
+ if sw_if_index:
+ if ep.endpoint.sw_if_index != sw_if_index:
+ continue
+ if sclass:
+ if ep.endpoint.sclass != sclass:
+ continue
+ if flags:
+ if flags != (flags & ep.endpoint.flags):
+ continue
+ if ip:
+ for eip in ep.endpoint.ips:
+ if vip == str(eip):
+ return True
+ if mac:
+ if vmac == ep.endpoint.mac:
+ return True
+
+ return False
+
+
+def find_gbp_vxlan(test: VppTestCase, vni):
+ ts = test.vapi.gbp_vxlan_tunnel_dump()
+ for t in ts:
+ if t.tunnel.vni == vni:
+ return True
+ return False
+
+
+class VppGbpEndpoint(VppObject):
+ """
+ GBP Endpoint
+ """
+
+ @property
+ def mac(self):
+ return str(self.vmac)
+
+ @property
+ def ip4(self):
+ return self._ip4
+
+ @property
+ def fip4(self):
+ return self._fip4
+
+ @property
+ def ip6(self):
+ return self._ip6
+
+ @property
+ def fip6(self):
+ return self._fip6
+
+ @property
+ def ips(self):
+ return [self.ip4, self.ip6]
+
+ @property
+ def fips(self):
+ return [self.fip4, self.fip6]
+
+ def __init__(
+ self,
+ test,
+ itf,
+ epg,
+ recirc,
+ ip4,
+ fip4,
+ ip6,
+ fip6,
+ flags=0,
+ tun_src="0.0.0.0",
+ tun_dst="0.0.0.0",
+ mac=True,
+ ):
+ self._test = test
+ self.itf = itf
+ self.handle = None
+ self.epg = epg
+ self.recirc = recirc
+
+ self._ip4 = ip4
+ self._fip4 = fip4
+ self._ip6 = ip6
+ self._fip6 = fip6
+
+ if mac:
+ self.vmac = MACAddress(self.itf.remote_mac)
+ else:
+ self.vmac = MACAddress("00:00:00:00:00:00")
+
+ self.flags = flags
+ self.tun_src = tun_src
+ self.tun_dst = tun_dst
+
+ def encode(self):
+ ips = [self.ip4, self.ip6]
+ return {
+ "sw_if_index": self.itf.sw_if_index,
+ "ips": ips,
+ "n_ips": len(ips),
+ "mac": self.vmac.packed,
+ "sclass": self.epg.sclass,
+ "flags": self.flags,
+ "tun": {
+ "src": self.tun_src,
+ "dst": self.tun_dst,
+ },
+ }
+
+ def add_vpp_config(self):
+ res = self._test.vapi.gbp_endpoint_add(
+ endpoint=self.encode(),
+ )
+ self.handle = res.handle
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_endpoint_del(handle=self.handle)
+
+ def object_id(self):
+ return "gbp-endpoint:[%d==%d:%s:%d]" % (
+ self.handle,
+ self.itf.sw_if_index,
+ self.ip4,
+ self.epg.sclass,
+ )
+
+ def query_vpp_config(self):
+ return find_gbp_endpoint(self._test, self.itf.sw_if_index, self.ip4)
+
+
+class VppGbpRecirc(VppObject):
+ """
+ GBP Recirculation Interface
+ """
+
+ def __init__(self, test, epg, recirc, is_ext=False):
+ self._test = test
+ self.recirc = recirc
+ self.epg = epg
+ self.is_ext = is_ext
+
+ def encode(self):
+ return {
+ "is_ext": self.is_ext,
+ "sw_if_index": self.recirc.sw_if_index,
+ "sclass": self.epg.sclass,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_recirc_add_del(
+ 1,
+ recirc=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_recirc_add_del(
+ 0,
+ recirc=self.encode(),
+ )
+
+ def object_id(self):
+ return "gbp-recirc:[%d]" % (self.recirc.sw_if_index)
+
+ def query_vpp_config(self):
+ rs = self._test.vapi.gbp_recirc_dump()
+ for r in rs:
+ if r.recirc.sw_if_index == self.recirc.sw_if_index:
+ return True
+ return False
+
+
+class VppGbpExtItf(VppObject):
+ """
+ GBP ExtItfulation Interface
+ """
+
+ def __init__(self, test, itf, bd, rd, anon=False):
+ self._test = test
+ self.itf = itf
+ self.bd = bd
+ self.rd = rd
+ self.flags = 1 if anon else 0
+
+ def encode(self):
+ return {
+ "sw_if_index": self.itf.sw_if_index,
+ "bd_id": self.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ "flags": self.flags,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_ext_itf_add_del(
+ 1,
+ ext_itf=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_ext_itf_add_del(
+ 0,
+ ext_itf=self.encode(),
+ )
+
+ def object_id(self):
+ return "gbp-ext-itf:[%d]%s" % (
+ self.itf.sw_if_index,
+ " [anon]" if self.flags else "",
+ )
+
+ def query_vpp_config(self):
+ rs = self._test.vapi.gbp_ext_itf_dump()
+ for r in rs:
+ if r.ext_itf.sw_if_index == self.itf.sw_if_index:
+ return True
+ return False
+
+
+class VppGbpSubnet(VppObject):
+ """
+ GBP Subnet
+ """
+
+ def __init__(
+ self,
+ test,
+ rd,
+ address,
+ address_len,
+ type,
+ sw_if_index=0xFFFFFFFF,
+ sclass=0xFFFF,
+ ):
+ # TODO: replace hardcoded defaults when vpp_papi supports
+ # defaults in typedefs
+ self._test = test
+ self.rd_id = rd.rd_id
+ a = ip_address(address)
+ if 4 == a.version:
+ self.prefix = IPv4Network("%s/%d" % (address, address_len), strict=False)
+ else:
+ self.prefix = IPv6Network("%s/%d" % (address, address_len), strict=False)
+ self.type = type
+ self.sw_if_index = sw_if_index
+ self.sclass = sclass
+
+ def encode(self):
+ return {
+ "type": self.type,
+ "sw_if_index": self.sw_if_index,
+ "sclass": self.sclass,
+ "prefix": self.prefix,
+ "rd_id": self.rd_id,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_subnet_add_del(
+ is_add=1,
+ subnet=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_subnet_add_del(is_add=0, subnet=self.encode())
+
+ def object_id(self):
+ return "gbp-subnet:[%d-%s]" % (self.rd_id, self.prefix)
+
+ def query_vpp_config(self):
+ ss = self._test.vapi.gbp_subnet_dump()
+ for s in ss:
+ if (
+ s.subnet.rd_id == self.rd_id
+ and s.subnet.type == self.type
+ and s.subnet.prefix == self.prefix
+ ):
+ return True
+ return False
+
+
+class VppGbpEndpointRetention(object):
+ def __init__(self, remote_ep_timeout=0xFFFFFFFF):
+ self.remote_ep_timeout = remote_ep_timeout
+
+ def encode(self):
+ return {"remote_ep_timeout": self.remote_ep_timeout}
+
+
+class VppGbpEndpointGroup(VppObject):
+ """
+ GBP Endpoint Group
+ """
+
+ def __init__(
+ self,
+ test,
+ vnid,
+ sclass,
+ rd,
+ bd,
+ uplink,
+ bvi,
+ bvi_ip4,
+ bvi_ip6=None,
+ retention=VppGbpEndpointRetention(),
+ ):
+ self._test = test
+ self.uplink = uplink
+ self.bvi = bvi
+ self.bvi_ip4 = bvi_ip4
+ self.bvi_ip6 = bvi_ip6
+ self.vnid = vnid
+ self.bd = bd # VppGbpBridgeDomain
+ self.rd = rd
+ self.sclass = sclass
+ if 0 == self.sclass:
+ self.sclass = 0xFFFF
+ self.retention = retention
+
+ def encode(self) -> dict:
+ return {
+ "uplink_sw_if_index": (
+ self.uplink.sw_if_index if self.uplink else INDEX_INVALID
+ ),
+ "bd_id": self.bd.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ "vnid": self.vnid,
+ "sclass": self.sclass,
+ "retention": self.retention.encode(),
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_endpoint_group_add(epg=self.encode())
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_endpoint_group_del(sclass=self.sclass)
+
+ def object_id(self) -> str:
+ return "gbp-endpoint-group:[%d]" % (self.vnid)
+
+ def query_vpp_config(self) -> bool:
+ epgs = self._test.vapi.gbp_endpoint_group_dump()
+ for epg in epgs:
+ if epg.epg.vnid == self.vnid:
+ return True
+ return False
+
+
+class VppGbpBridgeDomain(VppObject):
+ """
+ GBP Bridge Domain
+ """
+
+ def __init__(
+ self,
+ test,
+ bd,
+ rd,
+ bvi,
+ uu_fwd: typing.Optional[VppVxlanGbpTunnel] = None,
+ bm_flood=None,
+ learn=True,
+ uu_drop=False,
+ bm_drop=False,
+ ucast_arp=False,
+ ):
+ self._test = test
+ self.bvi = bvi
+ self.uu_fwd = uu_fwd
+ self.bm_flood = bm_flood
+ self.bd = bd
+ self.rd = rd
+
+ e = VppEnum.vl_api_gbp_bridge_domain_flags_t
+
+ self.flags = e.GBP_BD_API_FLAG_NONE
+ if not learn:
+ self.flags |= e.GBP_BD_API_FLAG_DO_NOT_LEARN
+ if uu_drop:
+ self.flags |= e.GBP_BD_API_FLAG_UU_FWD_DROP
+ if bm_drop:
+ self.flags |= e.GBP_BD_API_FLAG_MCAST_DROP
+ if ucast_arp:
+ self.flags |= e.GBP_BD_API_FLAG_UCAST_ARP
+
+ def encode(self) -> dict:
+ return {
+ "flags": self.flags,
+ "bvi_sw_if_index": self.bvi.sw_if_index,
+ "uu_fwd_sw_if_index": (
+ self.uu_fwd.sw_if_index if self.uu_fwd else INDEX_INVALID
+ ),
+ "bm_flood_sw_if_index": (
+ self.bm_flood.sw_if_index if self.bm_flood else INDEX_INVALID
+ ),
+ "bd_id": self.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_bridge_domain_add(
+ bd=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_bridge_domain_del(bd_id=self.bd.bd_id)
+
+ def object_id(self) -> str:
+ return "gbp-bridge-domain:[%d]" % (self.bd.bd_id)
+
+ def query_vpp_config(self) -> bool:
+ bds = self._test.vapi.gbp_bridge_domain_dump()
+ for bd in bds:
+ if bd.bd.bd_id == self.bd.bd_id:
+ return True
+ return False
+
+
+class VppGbpRouteDomain(VppObject):
+ """
+ GBP Route Domain
+ """
+
+ def __init__(self, test, rd_id, scope, t4, t6, ip4_uu=None, ip6_uu=None):
+ self._test = test
+ self.rd_id = rd_id
+ self.scope = scope
+ self.t4 = t4
+ self.t6 = t6
+ self.ip4_uu = ip4_uu
+ self.ip6_uu = ip6_uu
+
+ def encode(self) -> dict:
+ return {
+ "rd_id": self.rd_id,
+ "scope": self.scope,
+ "ip4_table_id": self.t4.table_id,
+ "ip6_table_id": self.t6.table_id,
+ "ip4_uu_sw_if_index": (
+ self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID
+ ),
+ "ip6_uu_sw_if_index": (
+ self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID
+ ),
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_route_domain_add(
+ rd=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_route_domain_del(rd_id=self.rd_id)
+
+ def object_id(self):
+ return "gbp-route-domain:[%d]" % (self.rd_id)
+
+ def query_vpp_config(self):
+ rds = self._test.vapi.gbp_route_domain_dump()
+ for rd in rds:
+ if rd.rd.rd_id == self.rd_id:
+ return True
+ return False
+
+
+class VppGbpContractNextHop:
+ def __init__(self, mac, bd, ip, rd):
+ self.mac = mac
+ self.ip = ip
+ self.bd = bd
+ self.rd = rd
+
+ def encode(self) -> dict:
+ return {
+ "ip": self.ip,
+ "mac": self.mac.packed,
+ "bd_id": self.bd.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ }
+
+
+class VppGbpContractRule:
+ def __init__(self, action, hash_mode, nhs=None):
+ self.action = action
+ self.hash_mode = hash_mode
+ self.nhs = [] if nhs is None else nhs
+
+ def encode(self) -> dict:
+ nhs = []
+ for nh in self.nhs:
+ nhs.append(nh.encode())
+ while len(nhs) < 8:
+ nhs.append({})
+ return {
+ "action": self.action,
+ "nh_set": {"hash_mode": self.hash_mode, "n_nhs": len(self.nhs), "nhs": nhs},
+ }
+
+ def __repr__(self):
+ return "<VppGbpContractRule action=%s, hash_mode=%s>" % (
+ self.action,
+ self.hash_mode,
+ )
+
+
+class VppGbpContract(VppObject):
+ """
+ GBP Contract
+ """
+
+ def __init__(
+ self,
+ test,
+ scope,
+ sclass,
+ dclass,
+ acl_index,
+ rules: list,
+ allowed_ethertypes: list,
+ ):
+ self._test = test
+ self.scope = scope
+ self.acl_index = acl_index
+ self.sclass = sclass
+ self.dclass = dclass
+ self.rules = rules
+ self.allowed_ethertypes = allowed_ethertypes
+ while len(self.allowed_ethertypes) < 16:
+ self.allowed_ethertypes.append(0)
+
+ def encode(self) -> dict:
+ rules = []
+ for r in self.rules:
+ rules.append(r.encode())
+ return {
+ "acl_index": self.acl_index,
+ "scope": self.scope,
+ "sclass": self.sclass,
+ "dclass": self.dclass,
+ "n_rules": len(rules),
+ "rules": rules,
+ "n_ether_types": len(self.allowed_ethertypes),
+ "allowed_ethertypes": self.allowed_ethertypes,
+ }
+
+ def add_vpp_config(self):
+ r = self._test.vapi.gbp_contract_add_del(is_add=1, contract=self.encode())
+
+ self.stats_index = r.stats_index
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_contract_add_del(
+ is_add=0,
+ contract=self.encode(),
+ )
+
+ def object_id(self):
+ return "gbp-contract:[%d:%d:%d:%d]" % (
+ self.scope,
+ self.sclass,
+ self.dclass,
+ self.acl_index,
+ )
+
+ def query_vpp_config(self):
+ cs = self._test.vapi.gbp_contract_dump()
+ for c in cs:
+ if (
+ c.contract.scope == self.scope
+ and c.contract.sclass == self.sclass
+ and c.contract.dclass == self.dclass
+ ):
+ return True
+ return False
+
+ def get_drop_stats(self):
+ c = self._test.statistics.get_counter("/net/gbp/contract/drop")
+ return c[0][self.stats_index]
+
+ def get_permit_stats(self):
+ c = self._test.statistics.get_counter("/net/gbp/contract/permit")
+ return c[0][self.stats_index]
+
+
+class VppGbpVxlanTunnel(VppInterface):
+ """
+ GBP VXLAN tunnel
+ """
+
+ def __init__(self, test, vni, bd_rd_id, mode, src):
+ super(VppGbpVxlanTunnel, self).__init__(test)
+ self._test = test
+ self.vni = vni
+ self.bd_rd_id = bd_rd_id
+ self.mode = mode
+ self.src = src
+
+ def encode(self) -> dict:
+ return {
+ "vni": self.vni,
+ "mode": self.mode,
+ "bd_rd_id": self.bd_rd_id,
+ "src": self.src,
+ }
+
+ def add_vpp_config(self):
+ r = self._test.vapi.gbp_vxlan_tunnel_add(
+ tunnel=self.encode(),
+ )
+ self.set_sw_if_index(r.sw_if_index)
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_vxlan_tunnel_del(vni=self.vni)
+
+ def object_id(self):
+ return "gbp-vxlan:%d" % (self.sw_if_index)
+
+ def query_vpp_config(self):
+ return find_gbp_vxlan(self._test, self.vni)
+
+
+@tag_fixme_vpp_workers
+class TestGBP(VppTestCase):
+ """GBP Test Case"""
+
+ @property
+ def nat_config_flags(self):
+ return VppEnum.vl_api_nat_config_flags_t
+
+ @property
+ def nat44_config_flags(self):
+ return VppEnum.vl_api_nat44_config_flags_t
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestGBP, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestGBP, cls).tearDownClass()
+
+ def setUp(self):
+ super(TestGBP, self).setUp()
+
+ self.create_pg_interfaces(range(9))
+ self.create_loopback_interfaces(8)
+
+ self.router_mac = MACAddress("00:11:22:33:44:55")
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ for i in self.lo_interfaces:
+ i.admin_up()
+
+ self.vlan_100 = VppDot1QSubint(self, self.pg0, 100)
+ self.vlan_100.admin_up()
+ self.vlan_101 = VppDot1QSubint(self, self.pg0, 101)
+ self.vlan_101.admin_up()
+ self.vlan_102 = VppDot1QSubint(self, self.pg0, 102)
+ self.vlan_102.admin_up()
+
+ def tearDown(self):
+ for i in self.pg_interfaces:
+ i.admin_down()
+ super(TestGBP, self).tearDown()
+ for i in self.lo_interfaces:
+ i.remove_vpp_config()
+ self.lo_interfaces = []
+ self.vlan_102.remove_vpp_config()
+ self.vlan_101.remove_vpp_config()
+ self.vlan_100.remove_vpp_config()
+
+ def send_and_expect_bridged(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_bridged6(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_routed(self, src, tx, dst, src_mac):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, src_mac)
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_routed6(self, src, tx, dst, src_mac):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, src_mac)
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_natted(self, src, tx, dst, src_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].src, src_ip)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_natted6(self, src, tx, dst, src_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].src, src_ip)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].dst, dst_ip)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ return rx
+
+ def send_and_expect_unnatted6(self, src, tx, dst, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].dst, dst_ip)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ return rx
+
+ def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, str(self.router_mac))
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IP].dst, dst_ip)
+ self.assertEqual(r[IP].src, src_ip)
+ return rx
+
+ def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, str(self.router_mac))
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IPv6].dst, dst_ip)
+ self.assertEqual(r[IPv6].src, src_ip)
+ return rx
+
+ def send_and_expect_no_arp(self, src, tx, dst):
+ self.pg_send(src, tx)
+ dst.get_capture(0, timeout=1)
+ dst.assert_nothing_captured(remark="")
+
+ def send_and_expect_arp(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[ARP].psrc, tx[0][ARP].psrc)
+ self.assertEqual(r[ARP].pdst, tx[0][ARP].pdst)
+ self.assertEqual(r[ARP].hwsrc, tx[0][ARP].hwsrc)
+ self.assertEqual(r[ARP].hwdst, tx[0][ARP].hwdst)
+ return rx
+
+ def test_gbp(self):
+ """Group Based Policy"""
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+
+ #
+ # Route Domains
+ #
+ gt4 = VppIpTable(self, 0)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 0, is_ip6=True)
+ gt6.add_vpp_config()
+ nt4 = VppIpTable(self, 20)
+ nt4.add_vpp_config()
+ nt6 = VppIpTable(self, 20, is_ip6=True)
+ nt6.add_vpp_config()
+
+ rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None)
+ rd20 = VppGbpRouteDomain(self, 20, 420, nt4, nt6, None, None)
+
+ rd0.add_vpp_config()
+ rd20.add_vpp_config()
+
+ #
+ # Bridge Domains
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd2 = VppBridgeDomain(self, 2)
+ bd20 = VppBridgeDomain(self, 20)
+
+ bd1.add_vpp_config()
+ bd2.add_vpp_config()
+ bd20.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0)
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1)
+ gbd20 = VppGbpBridgeDomain(self, bd20, rd20, self.loop2)
+
+ gbd1.add_vpp_config()
+ gbd2.add_vpp_config()
+ gbd20.add_vpp_config()
+
+ #
+ # 3 EPGs, 2 of which share a BD.
+ # 2 NAT EPGs, one for floating-IP subnets, the other for internet
+ #
+ epgs = [
+ VppGbpEndpointGroup(
+ self,
+ 220,
+ 1220,
+ rd0,
+ gbd1,
+ self.pg4,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 221,
+ 1221,
+ rd0,
+ gbd1,
+ self.pg5,
+ self.loop0,
+ "10.0.1.128",
+ "2001:10:1::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 222,
+ 1222,
+ rd0,
+ gbd2,
+ self.pg6,
+ self.loop1,
+ "10.0.2.128",
+ "2001:10:2::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 333,
+ 1333,
+ rd20,
+ gbd20,
+ self.pg7,
+ self.loop2,
+ "11.0.0.128",
+ "3001::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 444,
+ 1444,
+ rd20,
+ gbd20,
+ self.pg8,
+ self.loop2,
+ "11.0.0.129",
+ "3001::129",
+ ),
+ ]
+ recircs = [
+ VppGbpRecirc(self, epgs[0], self.loop3),
+ VppGbpRecirc(self, epgs[1], self.loop4),
+ VppGbpRecirc(self, epgs[2], self.loop5),
+ VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True),
+ VppGbpRecirc(self, epgs[4], self.loop7, is_ext=True),
+ ]
+
+ epg_nat = epgs[3]
+ recirc_nat = recircs[3]
+
+ #
+ # 4 end-points, 2 in the same subnet, 3 in the same BD
+ #
+ eps = [
+ VppGbpEndpoint(
+ self,
+ self.pg0,
+ epgs[0],
+ recircs[0],
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001::1",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg1,
+ epgs[0],
+ recircs[0],
+ "10.0.0.2",
+ "11.0.0.2",
+ "2001:10::2",
+ "3001::2",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg2,
+ epgs[1],
+ recircs[1],
+ "10.0.1.1",
+ "11.0.0.3",
+ "2001:10:1::1",
+ "3001::3",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg3,
+ epgs[2],
+ recircs[2],
+ "10.0.2.1",
+ "11.0.0.4",
+ "2001:10:2::1",
+ "3001::4",
+ ),
+ ]
+
+ self.vapi.nat44_ed_plugin_enable_disable(enable=1)
+ self.vapi.nat66_plugin_enable_disable(enable=1)
+
+ #
+ # Config related to each of the EPGs
+ #
+ for epg in epgs:
+ # IP config on the BVI interfaces
+ if epg != epgs[1] and epg != epgs[4]:
+ b4 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
+ b6 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
+ epg.bvi.set_mac(self.router_mac)
+
+ # The BVIs are NAT inside interfaces
+ flags = self.nat_config_flags.NAT_IS_INSIDE
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=epg.bvi.sw_if_index, flags=flags, is_add=1
+ )
+ self.vapi.nat66_add_del_interface(
+ sw_if_index=epg.bvi.sw_if_index, flags=flags, is_add=1
+ )
+
+ if_ip4 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip4, 32, bind=b4
+ ).add_vpp_config()
+ if_ip6 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip6, 128, bind=b6
+ ).add_vpp_config()
+
+ # EPG uplink interfaces in the RD
+ VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config()
+ VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config()
+
+ # add the BD ARP termination entry for BVI IP
+ epg.bd_arp_ip4 = VppBridgeDomainArpEntry(
+ self, epg.bd.bd, str(self.router_mac), epg.bvi_ip4
+ )
+ epg.bd_arp_ip6 = VppBridgeDomainArpEntry(
+ self, epg.bd.bd, str(self.router_mac), epg.bvi_ip6
+ )
+ epg.bd_arp_ip4.add_vpp_config()
+ epg.bd_arp_ip6.add_vpp_config()
+
+ # EPG in VPP
+ epg.add_vpp_config()
+
+ for recirc in recircs:
+ # EPG's ingress recirculation interface maps to its RD
+ VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t4).add_vpp_config()
+ VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t6).add_vpp_config()
+
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=recirc.recirc.sw_if_index, is_add=1
+ )
+ self.vapi.nat66_add_del_interface(
+ sw_if_index=recirc.recirc.sw_if_index, is_add=1
+ )
+
+ recirc.add_vpp_config()
+
+ for recirc in recircs:
+ self.assertTrue(
+ find_bridge_domain_port(
+ self, recirc.epg.bd.bd.bd_id, recirc.recirc.sw_if_index
+ )
+ )
+
+ for ep in eps:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ #
+ # routes to the endpoints. We need these since there are no
+ # adj-fibs due to the fact the the BVI address has /32 and
+ # the subnet is not attached.
+ #
+ for ip, fip in zip(ep.ips, ep.fips):
+ # Add static mappings for each EP from the 10/8 to 11/8 network
+ if ip_address(ip).version == 4:
+ flags = self.nat_config_flags.NAT_IS_ADDR_ONLY
+ self.vapi.nat44_add_del_static_mapping(
+ is_add=1,
+ local_ip_address=ip,
+ external_ip_address=fip,
+ external_sw_if_index=0xFFFFFFFF,
+ vrf_id=0,
+ flags=flags,
+ )
+ else:
+ self.vapi.nat66_add_del_static_mapping(
+ local_ip_address=ip, external_ip_address=fip, vrf_id=0, is_add=1
+ )
+
+ # VPP EP create ...
+ ep.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+
+ # ... results in a Gratuitous ARP/ND on the EPG's uplink
+ rx = ep.epg.uplink.get_capture(len(ep.ips) + 1, timeout=0.2)
+
+ for ii, ip in enumerate(ep.ips):
+ p = rx[ii]
+
+ if ip_address(ip).version == 6:
+ self.assertTrue(p.haslayer(ICMPv6ND_NA))
+ self.assertEqual(p[ICMPv6ND_NA].tgt, ip)
+ else:
+ self.assertTrue(p.haslayer(ARP))
+ self.assertEqual(p[ARP].psrc, ip)
+ self.assertEqual(p[ARP].pdst, ip)
+
+ # add the BD ARP termination entry for floating IP
+ for fip in ep.fips:
+ ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac, fip)
+ ba.add_vpp_config()
+
+ # floating IPs route via EPG recirc
+ r = VppIpRoute(
+ self,
+ fip,
+ ip_address(fip).max_prefixlen,
+ [
+ VppRoutePath(
+ fip,
+ ep.recirc.recirc.sw_if_index,
+ type=FibPathType.FIB_PATH_TYPE_DVR,
+ proto=get_dpo_proto(fip),
+ )
+ ],
+ table_id=20,
+ )
+ r.add_vpp_config()
+
+ # L2 FIB entries in the NAT EPG BD to bridge the packets from
+ # the outside direct to the internal EPG
+ lf = VppL2FibEntry(self, epg_nat.bd.bd, ep.mac, ep.recirc.recirc, bvi_mac=0)
+ lf.add_vpp_config()
+
+ self.assert_equal(
+ self.statistics["/net/arp/tx/gratuitous"][
+ :, epgs[0].uplink.sw_if_index
+ ].sum(),
+ 2,
+ )
+ self.assert_equal(
+ self.statistics["/net/arp/tx/gratuitous"][
+ :, epgs[1].uplink.sw_if_index
+ ].sum(),
+ 1,
+ )
+ self.assert_equal(
+ self.statistics["/net/ip6-nd/tx/gratuitous"][
+ :, epgs[0].uplink.sw_if_index
+ ].sum(),
+ 2,
+ )
+ self.assert_equal(
+ self.statistics["/net/ip6-nd/tx/gratuitous"][
+ :, epgs[1].uplink.sw_if_index
+ ].sum(),
+ 1,
+ )
+
+ #
+ # ARP packets for unknown IP are sent to the EPG uplink
+ #
+ pkt_arp = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
+ op="who-has",
+ hwdst="ff:ff:ff:ff:ff:ff",
+ hwsrc=self.pg0.remote_mac,
+ pdst="10.0.0.88",
+ psrc="10.0.0.99",
+ )
+
+ self.vapi.cli("clear trace")
+ self.pg0.add_stream(pkt_arp)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rxd = epgs[0].uplink.get_capture(1)
+
+ #
+ # ARP/ND packets get a response
+ #
+ pkt_arp = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
+ op="who-has",
+ hwdst="ff:ff:ff:ff:ff:ff",
+ hwsrc=self.pg0.remote_mac,
+ pdst=epgs[0].bvi_ip4,
+ psrc=eps[0].ip4,
+ )
+
+ self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
+
+ nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6))
+ d = inet_ntop(AF_INET6, nsma)
+ pkt_nd = (
+ Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac)
+ / IPv6(dst=d, src=eps[0].ip6)
+ / ICMPv6ND_NS(tgt=epgs[0].bvi_ip6)
+ / ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)
+ )
+ self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
+
+ #
+ # broadcast packets are flooded
+ #
+ pkt_bcast = (
+ Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac)
+ / IP(src=eps[0].ip4, dst="232.1.1.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.vapi.cli("clear trace")
+ self.pg0.add_stream(pkt_bcast)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rxd = eps[1].itf.get_capture(1)
+ self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+ rxd = epgs[0].uplink.get_capture(1)
+ self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+
+ #
+ # packets to non-local L3 destinations dropped
+ #
+ pkt_intra_epg_220_ip4 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt_inter_epg_222_ip4 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst="10.0.1.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * NUM_PKTS)
+
+ pkt_inter_epg_222_ip6 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst="2001:10::99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * NUM_PKTS)
+
+ #
+ # Add the subnet routes
+ #
+ s41 = VppGbpSubnet(
+ self,
+ rd0,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s42 = VppGbpSubnet(
+ self,
+ rd0,
+ "10.0.1.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s43 = VppGbpSubnet(
+ self,
+ rd0,
+ "10.0.2.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s61 = VppGbpSubnet(
+ self,
+ rd0,
+ "2001:10::1",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s62 = VppGbpSubnet(
+ self,
+ rd0,
+ "2001:10:1::1",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s63 = VppGbpSubnet(
+ self,
+ rd0,
+ "2001:10:2::1",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s41.add_vpp_config()
+ s42.add_vpp_config()
+ s43.add_vpp_config()
+ s61.add_vpp_config()
+ s62.add_vpp_config()
+ s63.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_intra_epg_220_ip4 * NUM_PKTS, eps[0].epg.uplink
+ )
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_222_ip4 * NUM_PKTS, eps[0].epg.uplink
+ )
+ self.send_and_expect_bridged6(
+ eps[0].itf, pkt_inter_epg_222_ip6 * NUM_PKTS, eps[0].epg.uplink
+ )
+
+ self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ self.logger.info(self.vapi.cli("sh gbp recirc"))
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("sh int addr"))
+ self.logger.info(self.vapi.cli("sh int feat loop6"))
+ self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
+ self.logger.info(self.vapi.cli("sh int feat loop3"))
+ self.logger.info(self.vapi.cli("sh int feat pg0"))
+
+ #
+ # Packet destined to unknown unicast is sent on the epg uplink ...
+ #
+ pkt_intra_epg_220_to_uplink = (
+ Ether(src=self.pg0.remote_mac, dst="00:00:00:33:44:55")
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_intra_epg_220_to_uplink * NUM_PKTS, eps[0].epg.uplink
+ )
+ # ... and nowhere else
+ self.pg1.get_capture(0, timeout=0.1)
+ self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
+
+ pkt_intra_epg_221_to_uplink = (
+ Ether(src=self.pg2.remote_mac, dst="00:00:00:33:44:66")
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(
+ eps[2].itf, pkt_intra_epg_221_to_uplink * NUM_PKTS, eps[2].epg.uplink
+ )
+
+ #
+ # Packets from the uplink are forwarded in the absence of a contract
+ #
+ pkt_intra_epg_220_from_uplink = (
+ Ether(src="00:00:00:33:44:55", dst=self.pg0.remote_mac)
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(
+ self.pg4, pkt_intra_epg_220_from_uplink * NUM_PKTS, self.pg0
+ )
+
+ #
+ # in the absence of policy, endpoints in the same EPG
+ # can communicate
+ #
+ pkt_intra_epg = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[1].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(self.pg0, pkt_intra_epg * NUM_PKTS, self.pg1)
+
+ #
+ # in the absence of policy, endpoints in the different EPG
+ # cannot communicate
+ #
+ pkt_inter_epg_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[2].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg0.remote_mac)
+ / IP(src=eps[2].ip4, dst=eps[0].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt_inter_epg_220_to_222 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=eps[3].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS)
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * NUM_PKTS)
+
+ #
+ # A uni-directional contract from EPG 220 -> 221
+ #
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule, rule2])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[1].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS, eps[2].itf
+ )
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * NUM_PKTS)
+
+ #
+ # contract for the return direction
+ #
+ c2 = VppGbpContract(
+ self,
+ 400,
+ epgs[1].sclass,
+ epgs[0].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS, eps[2].itf
+ )
+ self.send_and_expect_bridged(
+ eps[2].itf, pkt_inter_epg_221_to_220 * NUM_PKTS, eps[0].itf
+ )
+
+ ds = c2.get_drop_stats()
+ self.assertEqual(ds["packets"], 0)
+ ps = c2.get_permit_stats()
+ self.assertEqual(ps["packets"], NUM_PKTS)
+
+ #
+ # the contract does not allow non-IP
+ #
+ pkt_non_ip_inter_epg_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac) / ARP()
+ )
+ self.send_and_assert_no_replies(
+ eps[0].itf, pkt_non_ip_inter_epg_220_to_221 * 17
+ )
+
+ #
+ # check that inter group is still disabled for the groups
+ # not in the contract.
+ #
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * NUM_PKTS)
+
+ #
+ # A uni-directional contract from EPG 220 -> 222 'L3 routed'
+ #
+ c3 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[2].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh gbp contract"))
+
+ self.send_and_expect_routed(
+ eps[0].itf,
+ pkt_inter_epg_220_to_222 * NUM_PKTS,
+ eps[3].itf,
+ str(self.router_mac),
+ )
+ #
+ # remove both contracts, traffic stops in both directions
+ #
+ c2.remove_vpp_config()
+ c1.remove_vpp_config()
+ c3.remove_vpp_config()
+ acl.remove_vpp_config()
+
+ self.send_and_assert_no_replies(eps[2].itf, pkt_inter_epg_221_to_220 * NUM_PKTS)
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS)
+ self.send_and_expect_bridged(eps[0].itf, pkt_intra_epg * NUM_PKTS, eps[1].itf)
+
+ #
+ # EPs to the outside world
+ #
+
+ # in the EP's RD an external subnet via the NAT EPG's recirc
+ se1 = VppGbpSubnet(
+ self,
+ rd0,
+ "0.0.0.0",
+ 0,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+ sw_if_index=recirc_nat.recirc.sw_if_index,
+ sclass=epg_nat.sclass,
+ )
+ se2 = VppGbpSubnet(
+ self,
+ rd0,
+ "11.0.0.0",
+ 8,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+ sw_if_index=recirc_nat.recirc.sw_if_index,
+ sclass=epg_nat.sclass,
+ )
+ se16 = VppGbpSubnet(
+ self,
+ rd0,
+ "::",
+ 0,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+ sw_if_index=recirc_nat.recirc.sw_if_index,
+ sclass=epg_nat.sclass,
+ )
+ # in the NAT RD an external subnet via the NAT EPG's uplink
+ se3 = VppGbpSubnet(
+ self,
+ rd20,
+ "0.0.0.0",
+ 0,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+ sw_if_index=epg_nat.uplink.sw_if_index,
+ sclass=epg_nat.sclass,
+ )
+ se36 = VppGbpSubnet(
+ self,
+ rd20,
+ "::",
+ 0,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+ sw_if_index=epg_nat.uplink.sw_if_index,
+ sclass=epg_nat.sclass,
+ )
+ se4 = VppGbpSubnet(
+ self,
+ rd20,
+ "11.0.0.0",
+ 8,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+ sw_if_index=epg_nat.uplink.sw_if_index,
+ sclass=epg_nat.sclass,
+ )
+ se1.add_vpp_config()
+ se2.add_vpp_config()
+ se16.add_vpp_config()
+ se3.add_vpp_config()
+ se36.add_vpp_config()
+ se4.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
+ self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
+ self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
+ self.logger.info(self.vapi.cli("sh ip6 fib %s" % eps[0].fip6))
+
+ #
+ # From an EP to an outside address: IN2OUT
+ #
+ pkt_inter_epg_220_to_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst="1.1.1.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ # no policy yet
+ self.send_and_assert_no_replies(
+ eps[0].itf, pkt_inter_epg_220_to_global * NUM_PKTS
+ )
+ rule = AclRule(is_permit=1, proto=17, ports=1234)
+ rule2 = AclRule(
+ is_permit=1,
+ proto=17,
+ ports=1234,
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ )
+ acl2 = VppAcl(self, rules=[rule, rule2])
+ acl2.add_vpp_config()
+
+ c4 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[3].sclass,
+ acl2.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c4.add_vpp_config()
+
+ self.send_and_expect_natted(
+ eps[0].itf, pkt_inter_epg_220_to_global * NUM_PKTS, self.pg7, eps[0].fip4
+ )
+
+ pkt_inter_epg_220_to_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst="6001::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_natted6(
+ self.pg0, pkt_inter_epg_220_to_global * NUM_PKTS, self.pg7, eps[0].fip6
+ )
+ #
+ # From a global address to an EP: OUT2IN
+ #
+ pkt_inter_epg_220_from_global = (
+ Ether(src=str(self.router_mac), dst=self.pg0.remote_mac)
+ / IP(dst=eps[0].fip4, src="1.1.1.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(
+ self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS
+ )
+
+ c5 = VppGbpContract(
+ self,
+ 400,
+ epgs[3].sclass,
+ epgs[0].sclass,
+ acl2.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c5.add_vpp_config()
+
+ self.send_and_expect_unnatted(
+ self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS, eps[0].itf, eps[0].ip4
+ )
+
+ pkt_inter_epg_220_from_global = (
+ Ether(src=str(self.router_mac), dst=self.pg0.remote_mac)
+ / IPv6(dst=eps[0].fip6, src="6001::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_unnatted6(
+ self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS, eps[0].itf, eps[0].ip6
+ )
+
+ #
+ # From a local VM to another local VM using resp. public addresses:
+ # IN2OUT2IN
+ #
+ pkt_intra_epg_220_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=eps[1].fip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_double_natted(
+ eps[0].itf,
+ pkt_intra_epg_220_global * NUM_PKTS,
+ eps[1].itf,
+ eps[0].fip4,
+ eps[1].ip4,
+ )
+
+ pkt_intra_epg_220_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst=eps[1].fip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_double_natted6(
+ eps[0].itf,
+ pkt_intra_epg_220_global * NUM_PKTS,
+ eps[1].itf,
+ eps[0].fip6,
+ eps[1].ip6,
+ )
+
+ #
+ # cleanup
+ #
+ self.vapi.nat44_ed_plugin_enable_disable(enable=0)
+ self.vapi.nat66_plugin_enable_disable(enable=0)
+
+ def wait_for_ep_timeout(
+ self, sw_if_index=None, ip=None, mac=None, tep=None, n_tries=100, s_time=1
+ ):
+ # only learnt EP can timeout
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ flags = ep_flags.GBP_API_ENDPOINT_FLAG_LEARNT
+ while n_tries:
+ if not find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep, flags=flags):
+ return True
+ n_tries = n_tries - 1
+ self.sleep(s_time)
+ self.assertFalse(
+ find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep, flags=flags)
+ )
+ return False
+
+ def test_gbp_learn_l2(self):
+ """GBP L2 Endpoint Learning"""
+
+ drop_no_contract = self.statistics.get_err_counter(
+ "/err/gbp-policy-port/drop-no-contract"
+ )
+ allow_intra_class = self.statistics.get_err_counter(
+ "/err/gbp-policy-port/allow-intra-sclass"
+ )
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ learnt = [
+ {"mac": "00:00:11:11:11:01", "ip": "10.0.0.1", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.0.2", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
+ # Pg3 hosts the IP4 UU-flood VXLAN tunnel
+ # Pg4 hosts the IP6 UU-flood VXLAN tunnel
+ #
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg2.generate_remote_hosts(4)
+ self.pg2.configure_ipv4_neighbors()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+ self.pg4.config_ip4()
+ self.pg4.resolve_arp()
+
+ #
+ # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
+ #
+ tun_bm = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, "239.1.1.1", 88, mcast_itf=self.pg4
+ )
+ tun_bm.add_vpp_config()
+
+ #
+ # a GBP bridge domain with a BVI and a UU-flood interface
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, self.pg3, tun_bm)
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
+ ip_addr.add_vpp_config()
+
+ #
+ # The Endpoint-group in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 112,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+ epg_330 = VppGbpEndpointGroup(
+ self,
+ 330,
+ 113,
+ rd1,
+ gbd1,
+ None,
+ self.loop1,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_330.add_vpp_config()
+
+ #
+ # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
+ # learning enabled
+ #
+ vx_tun_l2_1 = VppGbpVxlanTunnel(
+ self,
+ 99,
+ bd1.bd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l2_1.add_vpp_config()
+
+ #
+ # A static endpoint that the learnt endpoints are trying to
+ # talk to
+ #
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ self.assertTrue(find_route(self, ep.ip4, 32, table_id=1))
+
+ # a packet with an sclass from an unknown EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[0].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=88, flags=0x88)
+ / Ether(src=learnt[0]["mac"], dst=ep.mac)
+ / IP(src=learnt[0]["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg2, p)
+
+ self.logger.info(self.vapi.cli("sh error"))
+ self.assert_error_counter_equal(
+ "/err/gbp-policy-port/drop-no-contract", drop_no_contract + 1
+ )
+
+ #
+ # we should not have learnt a new tunnel endpoint, since
+ # the EPG was not learnt.
+ #
+ self.assertEqual(
+ INDEX_INVALID,
+ find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[0].ip4, 99
+ ),
+ )
+
+ # ep is not learnt, because the EPG is unknown
+ self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
+
+ #
+ # Learn new EPs from IP packets
+ #
+ for ii, l in enumerate(learnt):
+ # a packet with an sclass from a known EPG
+ # arriving on an unknown TEP
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=112, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # the new TEP
+ tep1_sw_if_index = find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, 99
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ #
+ # the EP is learnt via the learnt TEP
+ # both from its MAC and its IP
+ #
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, ip=l["ip"])
+ )
+
+ self.assert_error_counter_equal(
+ "/err/gbp-policy-port/allow-intra-sclass", allow_intra_class + 2
+ )
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+ self.logger.info(self.vapi.cli("show gbp vxlan"))
+ self.logger.info(self.vapi.cli("show ip mfib"))
+
+ #
+ # If we sleep for the threshold time, the learnt endpoints should
+ # age out
+ #
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+
+ #
+ # Learn new EPs from GARP packets received on the BD's mcast tunnel
+ #
+ for ii, l in enumerate(learnt):
+ # add some junk in the reserved field of the vxlan-header
+ # next to the VNI. we should accept since reserved bits are
+ # ignored on rx.
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst="239.1.1.1")
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=88, reserved2=0x80, gpid=112, flags=0x88)
+ / Ether(src=l["mac"], dst="ff:ff:ff:ff:ff:ff")
+ / ARP(
+ op="who-has",
+ psrc=l["ip"],
+ pdst=l["ip"],
+ hwsrc=l["mac"],
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+
+ rx = self.send_and_expect(self.pg4, [p], self.pg0)
+
+ # the new TEP
+ tep1_sw_if_index = find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, 99
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ #
+ # the EP is learnt via the learnt TEP
+ # both from its MAC and its IP
+ #
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, ip=l["ip"])
+ )
+
+ #
+ # wait for the learnt endpoints to age out
+ #
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+
+ #
+ # Learn new EPs from L2 packets
+ #
+ for ii, l in enumerate(learnt):
+ # a packet with an sclass from a known EPG
+ # arriving on an unknown TEP
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=112, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # the new TEP
+ tep1_sw_if_index = find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, 99
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ #
+ # the EP is learnt via the learnt TEP
+ # both from its MAC and its IP
+ #
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+ self.logger.info(self.vapi.cli("show gbp vxlan"))
+ self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+
+ #
+ # wait for the learnt endpoints to age out
+ #
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+
+ #
+ # repeat. the do not learn bit is set so the EPs are not learnt
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=112, flags=0x88, gpflags="D")
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ for l in learnt:
+ self.assertFalse(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # repeat
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ # set a reserved bit in addition to the G and I
+ # reserved bits should not be checked on rx.
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=112, flags=0xC8)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # Static EP replies to dynamics
+ #
+ self.logger.info(self.vapi.cli("sh l2fib bd_id 1"))
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 17, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 112)
+ self.assertEqual(rx[VXLAN].vni, 99)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+
+ #
+ # repeat in the other EPG
+ # there's no contract between 220 and 330, but the A-bit is set
+ # so the packet is cleared for delivery
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=113, flags=0x88, gpflags="A")
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # static EP cannot reach the learnt EPs since there is no contract
+ # only test 1 EP as the others could timeout
+ #
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IP(dst=learnt[0]["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, [p])
+
+ #
+ # refresh the entries after the check for no replies above
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=113, flags=0x88, gpflags="A")
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # Add the contract so they can talk
+ #
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule, rule2])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 401,
+ epg_220.sclass,
+ epg_330.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect(self.pg0, [p], self.pg2)
+
+ #
+ # send UU packets from the local EP
+ #
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ self.logger.info(self.vapi.cli("sh bridge-domain 1 detail"))
+ p_uu = (
+ Ether(src=ep.mac, dst="00:11:11:11:11:11")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_fwd)
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+
+ p_bm = (
+ Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg4.local_ip4)
+ self.assertEqual(rx[IP].dst, "239.1.1.1")
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 112)
+ self.assertEqual(rx[VXLAN].vni, 88)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertFalse(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule, rule2])
+ acl.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 401,
+ epg_330.sclass,
+ epg_220.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ #
+ # Check v6 Endpoints learning
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=113, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l2_1.sw_if_index,
+ ip=l["ip6"],
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4],
+ )
+ )
+
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+ self.logger.info(self.vapi.cli("sh gbp vxlan"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ self.logger.info(self.vapi.cli("sh gbp interface"))
+
+ #
+ # EP moves to a different TEP
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=113, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l2_1.sw_if_index,
+ sclass=113,
+ mac=l["mac"],
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4],
+ )
+ )
+
+ #
+ # v6 remote EP reachability
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IPv6(dst=l["ip6"], src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 112)
+ self.assertEqual(rx[VXLAN].vni, 99)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ self.assertEqual(rx[IPv6].dst, l["ip6"])
+
+ #
+ # EP changes sclass
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=112, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l2_1.sw_if_index,
+ mac=l["mac"],
+ sclass=112,
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4],
+ )
+ )
+
+ #
+ # check reachability and contract intra-epg
+ #
+ allow_intra_class = self.statistics.get_err_counter(
+ "/err/gbp-policy-mac/allow-intra-sclass"
+ )
+
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IPv6(dst=l["ip6"], src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ self.assertEqual(rx[VXLAN].gpid, 112)
+ self.assertEqual(rx[VXLAN].vni, 99)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ self.assertEqual(rx[IPv6].dst, l["ip6"])
+
+ allow_intra_class += NUM_PKTS
+
+ self.assert_error_counter_equal(
+ "/err/gbp-policy-mac/allow-intra-sclass", allow_intra_class
+ )
+
+ #
+ # clean up
+ #
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ self.pg2.unconfig_ip4()
+ self.pg3.unconfig_ip4()
+ self.pg4.unconfig_ip4()
+
+ def test_gbp_contract(self):
+ """GBP Contracts"""
+
+ #
+ # Route Domains
+ #
+ gt4 = VppIpTable(self, 0)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 0, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None)
+
+ rd0.add_vpp_config()
+
+ #
+ # Bridge Domains
+ #
+ bd1 = VppBridgeDomain(self, 1, arp_term=0)
+ bd2 = VppBridgeDomain(self, 2, arp_term=0)
+
+ bd1.add_vpp_config()
+ bd2.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0)
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1)
+
+ gbd1.add_vpp_config()
+ gbd2.add_vpp_config()
+
+ #
+ # 3 EPGs, 2 of which share a BD.
+ #
+ epgs = [
+ VppGbpEndpointGroup(
+ self,
+ 220,
+ 1220,
+ rd0,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 221,
+ 1221,
+ rd0,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.1.128",
+ "2001:10:1::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 222,
+ 1222,
+ rd0,
+ gbd2,
+ None,
+ self.loop1,
+ "10.0.2.128",
+ "2001:10:2::128",
+ ),
+ ]
+ #
+ # 4 end-points, 2 in the same subnet, 3 in the same BD
+ #
+ eps = [
+ VppGbpEndpoint(
+ self,
+ self.pg0,
+ epgs[0],
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001::1",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg1,
+ epgs[0],
+ None,
+ "10.0.0.2",
+ "11.0.0.2",
+ "2001:10::2",
+ "3001::2",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg2,
+ epgs[1],
+ None,
+ "10.0.1.1",
+ "11.0.0.3",
+ "2001:10:1::1",
+ "3001::3",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg3,
+ epgs[2],
+ None,
+ "10.0.2.1",
+ "11.0.0.4",
+ "2001:10:2::1",
+ "3001::4",
+ ),
+ ]
+
+ #
+ # Config related to each of the EPGs
+ #
+ for epg in epgs:
+ # IP config on the BVI interfaces
+ if epg != epgs[1]:
+ b4 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
+ b6 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
+ epg.bvi.set_mac(self.router_mac)
+
+ if_ip4 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip4, 32, bind=b4
+ ).add_vpp_config()
+ if_ip6 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip6, 128, bind=b6
+ ).add_vpp_config()
+
+ # add the BD ARP termination entry for BVI IP
+ epg.bd_arp_ip4 = VppBridgeDomainArpEntry(
+ self, epg.bd.bd, str(self.router_mac), epg.bvi_ip4
+ )
+ epg.bd_arp_ip4.add_vpp_config()
+
+ # EPG in VPP
+ epg.add_vpp_config()
+
+ #
+ # config ep
+ #
+ for ep in eps:
+ ep.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+ self.logger.info(self.vapi.cli("show interface"))
+ self.logger.info(self.vapi.cli("show br"))
+
+ #
+ # Intra epg allowed without contract
+ #
+ pkt_intra_epg_220_to_220 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[1].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(self.pg0, pkt_intra_epg_220_to_220 * 65, self.pg1)
+
+ pkt_intra_epg_220_to_220 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac)
+ / IPv6(src=eps[0].ip6, dst=eps[1].ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged6(self.pg0, pkt_intra_epg_220_to_220 * 65, self.pg1)
+
+ #
+ # Inter epg denied without contract
+ #
+ pkt_inter_epg_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[2].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_221)
+
+ #
+ # A uni-directional contract from EPG 220 -> 221
+ #
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ rule3 = AclRule(is_permit=1, proto=1)
+ acl = VppAcl(self, rules=[rule, rule2, rule3])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[1].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * 65, eps[2].itf
+ )
+
+ pkt_inter_epg_220_to_222 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=eps[3].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * 65)
+
+ #
+ # ping router IP in different BD
+ #
+ pkt_router_ping_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=epgs[1].bvi_ip4)
+ / ICMP(type="echo-request")
+ )
+
+ self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0)
+
+ pkt_router_ping_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst=epgs[1].bvi_ip6)
+ / ICMPv6EchoRequest()
+ )
+
+ self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0)
+
+ #
+ # contract for the return direction
+ #
+ c2 = VppGbpContract(
+ self,
+ 400,
+ epgs[1].sclass,
+ epgs[0].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * 65, eps[2].itf
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg0.remote_mac)
+ / IP(src=eps[2].ip4, dst=eps[0].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_expect_bridged(
+ eps[2].itf, pkt_inter_epg_221_to_220 * 65, eps[0].itf
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[2].ip4, dst=eps[0].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_expect_routed(
+ eps[2].itf, pkt_inter_epg_221_to_220 * 65, eps[0].itf, str(self.router_mac)
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[2].ip6, dst=eps[0].ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_expect_routed6(
+ eps[2].itf, pkt_inter_epg_221_to_220 * 65, eps[0].itf, str(self.router_mac)
+ )
+
+ #
+ # contract between 220 and 222 uni-direction
+ #
+ c3 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[2].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ self.send_and_expect(eps[0].itf, pkt_inter_epg_220_to_222 * 65, eps[3].itf)
+
+ c3.remove_vpp_config()
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+ acl.remove_vpp_config()
+
+ def test_gbp_bd_drop_flags(self):
+ """GBP BD drop flags"""
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # a GBP bridge domain with a BVI only
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(
+ self, bd1, rd1, self.loop0, None, None, uu_drop=True, bm_drop=True
+ )
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 112,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(3),
+ )
+ epg_220.add_vpp_config()
+
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ #
+ # send UU/BM packet from the local EP with UU drop and BM drop enabled
+ # in bd
+ #
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ p_uu = (
+ Ether(src=ep.mac, dst="00:11:11:11:11:11")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(ep.itf, [p_uu])
+
+ p_bm = (
+ Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(ep.itf, [p_bm])
+
+ self.pg3.unconfig_ip4()
+
+ self.logger.info(self.vapi.cli("sh int"))
+
+ def test_gbp_bd_arp_flags(self):
+ """GBP BD arp flags"""
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # Pg4 hosts the IP6 UU-flood VXLAN tunnel
+ #
+ self.pg4.config_ip4()
+ self.pg4.resolve_arp()
+
+ #
+ # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
+ #
+ tun_uu = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, "239.1.1.1", 88, mcast_itf=self.pg4
+ )
+ tun_uu.add_vpp_config()
+
+ #
+ # a GBP bridge domain with a BVI and a UU-flood interface
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(
+ self, bd1, rd1, self.loop0, tun_uu, None, ucast_arp=True
+ )
+ gbd1.add_vpp_config()
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 112,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(2),
+ )
+ epg_220.add_vpp_config()
+
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ #
+ # send ARP packet from the local EP expect it on the uu interface
+ #
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ p_arp = Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
+ op="who-has",
+ psrc=ep.ip4,
+ pdst="10.0.0.99",
+ hwsrc=ep.mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ self.send_and_expect(ep.itf, [p_arp], self.pg4)
+
+ self.pg4.unconfig_ip4()
+
+ def test_gbp_learn_vlan_l2(self):
+ """GBP L2 Endpoint w/ VLANs"""
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ learnt = [
+ {"mac": "00:00:11:11:11:01", "ip": "10.0.0.1", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.0.2", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
+ #
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg2.generate_remote_hosts(4)
+ self.pg2.configure_ipv4_neighbors()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+
+ #
+ # The EP will be on a vlan sub-interface
+ #
+ vlan_11 = VppDot1QSubint(self, self.pg0, 11)
+ vlan_11.admin_up()
+ self.vapi.l2_interface_vlan_tag_rewrite(
+ sw_if_index=vlan_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, push_dot1q=11
+ )
+
+ bd_uu_fwd = VppVxlanGbpTunnel(
+ self, self.pg3.local_ip4, self.pg3.remote_ip4, 116
+ )
+ bd_uu_fwd.add_vpp_config()
+
+ #
+ # a GBP bridge domain with a BVI and a UU-flood interface
+ # The BD is marked as do not learn, so no endpoints are ever
+ # learnt in this BD.
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, bd_uu_fwd, learn=False)
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 441,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+
+ #
+ # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
+ # learning enabled
+ #
+ vx_tun_l2_1 = VppGbpVxlanTunnel(
+ self,
+ 99,
+ bd1.bd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l2_1.add_vpp_config()
+
+ #
+ # A static endpoint that the learnt endpoints are trying to
+ # talk to
+ #
+ ep = VppGbpEndpoint(
+ self,
+ vlan_11,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ self.assertTrue(find_route(self, ep.ip4, 32, table_id=1))
+
+ #
+ # Send to the static EP
+ #
+ for ii, l in enumerate(learnt):
+ # a packet with an sclass from a known EPG
+ # arriving on an unknown TEP
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ #
+ # packet to EP has the EP's vlan tag
+ #
+ for rx in rxs:
+ self.assertEqual(rx[Dot1Q].vlan, 11)
+
+ #
+ # the EP is not learnt since the BD setting prevents it
+ # also no TEP too
+ #
+ self.assertFalse(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+ self.assertEqual(
+ INDEX_INVALID,
+ find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, 99
+ ),
+ )
+
+ self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
+
+ #
+ # static to remotes
+ # we didn't learn the remotes so they are sent to the UU-fwd
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / Dot1Q(vlan=11)
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 17, self.pg3)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg3.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg3.remote_ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 441)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertFalse(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ self.pg2.unconfig_ip4()
+ self.pg3.unconfig_ip4()
+
+ def test_gbp_learn_l3(self):
+ """GBP L3 Endpoint Learning"""
+
+ self.vapi.cli("set logging class gbp level debug")
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ learnt = [
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.1.2", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:03", "ip": "10.0.1.3", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ tun_ip4_uu = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, self.pg4.remote_ip4, 114
+ )
+ tun_ip6_uu = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, self.pg4.remote_ip4, 116
+ )
+ tun_ip4_uu.add_vpp_config()
+ tun_ip6_uu.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 401, t4, t6, tun_ip4_uu, tun_ip6_uu)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg2 hosts the vxlan tunnel
+ # hosts on pg2 to act as TEPs
+ # pg3 is BD uu-fwd
+ # pg4 is RD uu-fwd
+ #
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg2.generate_remote_hosts(4)
+ self.pg2.configure_ipv4_neighbors()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+ self.pg4.config_ip4()
+ self.pg4.resolve_arp()
+
+ #
+ # a GBP bridge domain with a BVI and a UU-flood interface
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, self.pg3)
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ self.logger.info(self.vapi.cli("sh gbp route"))
+
+ # ... and has a /32 and /128 applied
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32, bind=b4
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 128, bind=b6
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 441,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+
+ #
+ # The VXLAN GBP tunnel is in L3 mode with learning enabled
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self,
+ 101,
+ rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l3.add_vpp_config()
+
+ #
+ # A static endpoint that the learnt endpoints are trying to
+ # talk to
+ #
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ #
+ # learn some remote IPv4 EPs
+ #
+ for ii, l in enumerate(learnt):
+ # a packet with an sclass from a known EPG
+ # arriving on an unknown TEP
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # the new TEP
+ tep1_sw_if_index = find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, vx_tun_l3.vni
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip=l["ip"]))
+
+ #
+ # Static IPv4 EP replies to learnt
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 441)
+ self.assertEqual(rx[VXLAN].vni, 101)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep.ip4)
+ self.assertEqual(inner[IP].dst, l["ip"])
+
+ for l in learnt:
+ self.assertFalse(find_gbp_endpoint(self, tep1_sw_if_index, ip=l["ip"]))
+
+ #
+ # learn some remote IPv6 EPs
+ #
+ for ii, l in enumerate(learnt):
+ # a packet with an sclass from a known EPG
+ # arriving on an unknown TEP
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # the new TEP
+ tep1_sw_if_index = find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, vx_tun_l3.vni
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ self.logger.info(self.vapi.cli("show gbp bridge"))
+ self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+ self.logger.info(self.vapi.cli("show gbp vxlan"))
+ self.logger.info(self.vapi.cli("show int addr"))
+
+ # endpoint learnt via the TEP
+ self.assertTrue(find_gbp_endpoint(self, ip=l["ip6"]))
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+ self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l["ip"]))
+
+ #
+ # Static EP replies to learnt
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IPv6(dst=l["ip6"], src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 441)
+ self.assertEqual(rx[VXLAN].vni, 101)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IPv6].src, ep.ip6)
+ self.assertEqual(inner[IPv6].dst, l["ip6"])
+
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ for l in learnt:
+ self.wait_for_ep_timeout(ip=l["ip"])
+
+ #
+ # Static sends to unknown EP with no route
+ #
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst="10.0.0.99", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, [p])
+
+ #
+ # Add a route to static EP's v4 and v6 subnet
+ #
+ se_10_24 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ )
+ se_10_24.add_vpp_config()
+
+ #
+ # static pings router
+ #
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst=epg_220.bvi_ip4, src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0)
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IPv6(dst=epg_220.bvi_ip6, src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0)
+
+ #
+ # packets to address in the subnet are sent on the uu-fwd
+ #
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst="10.0.0.99", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg4.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg4.remote_ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 441)
+ self.assertEqual(rx[VXLAN].vni, 114)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # policy is not applied to packets sent to the uu-fwd interfaces
+ self.assertFalse(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ #
+ # learn some remote IPv4 EPs
+ #
+ for ii, l in enumerate(learnt):
+ # a packet with an sclass from a known EPG
+ # arriving on an unknown TEP
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # the new TEP
+ tep1_sw_if_index = find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4, vx_tun_l3.vni
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip=l["ip"]))
+
+ #
+ # Add a remote endpoint from the API
+ #
+ rep_88 = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.0.88",
+ "11.0.0.88",
+ "2001:10::88",
+ "3001::88",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg2.local_ip4,
+ self.pg2.remote_hosts[2].ip4,
+ mac=None,
+ )
+ rep_88.add_vpp_config()
+
+ #
+ # Add a remote endpoint from the API that matches an existing one
+ # this is a lower priority, hence the packet is sent to the DP leanrt
+ # TEP
+ #
+ rep_2 = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ learnt[0]["ip"],
+ "11.0.0.101",
+ learnt[0]["ip6"],
+ "3001::101",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg2.local_ip4,
+ self.pg2.remote_hosts[1].ip4,
+ mac=None,
+ )
+ rep_2.add_vpp_config()
+
+ #
+ # Add a route to the learned EP's v4 subnet
+ # packets should be send on the v4/v6 uu=fwd interface resp.
+ #
+ se_10_1_24 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.1.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ )
+ se_10_1_24.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+
+ ips = ["10.0.0.88", learnt[0]["ip"]]
+ for ip in ips:
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst=ip, src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 441)
+ self.assertEqual(rx[VXLAN].vni, 101)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep.ip4)
+ self.assertEqual(inner[IP].dst, ip)
+
+ #
+ # remove the API remote EPs, only API sourced is gone, the DP
+ # learnt one remains
+ #
+ rep_88.remove_vpp_config()
+ rep_2.remove_vpp_config()
+
+ self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4))
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(src=ep.ip4, dst=rep_2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg0, [p], self.pg2)
+
+ self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4))
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(src=ep.ip4, dst=rep_88.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+
+ #
+ # to appease the testcase we cannot have the registered EP still
+ # present (because it's DP learnt) when the TC ends so wait until
+ # it is removed
+ #
+ self.wait_for_ep_timeout(ip=rep_88.ip4)
+ self.wait_for_ep_timeout(ip=rep_2.ip4)
+
+ #
+ # Same as above, learn a remote EP via CP and DP
+ # this time remove the DP one first. expect the CP data to remain
+ #
+ rep_3 = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.1.4",
+ "11.0.0.103",
+ "2001::10:3",
+ "3001::103",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg2.local_ip4,
+ self.pg2.remote_hosts[1].ip4,
+ mac=None,
+ )
+ rep_3.add_vpp_config()
+
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src="10.0.1.4", dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l3._sw_if_index,
+ ip=rep_3.ip4,
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4],
+ )
+ )
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst="10.0.1.4", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ # host 2 is the DP learned TEP
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+
+ self.wait_for_ep_timeout(
+ ip=rep_3.ip4, tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4]
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ # host 1 is the CP learned TEP
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+
+ #
+ # shutdown with learnt endpoint present
+ #
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[1].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src=learnt[1]["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip=l["ip"]))
+
+ #
+ # TODO
+ # remote endpoint becomes local
+ #
+ self.pg2.unconfig_ip4()
+ self.pg3.unconfig_ip4()
+ self.pg4.unconfig_ip4()
+
+ def test_gbp_redirect(self):
+ """GBP Endpoint Redirect"""
+
+ self.vapi.cli("set logging class gbp level debug")
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ learnt = [
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.1.2", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:03", "ip": "10.0.1.3", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's UU-fwd
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a GBP bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
+ gbd1.add_vpp_config()
+
+ bd2 = VppBridgeDomain(self, 2)
+ bd2.add_vpp_config()
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
+ gbd2.add_vpp_config()
+
+ # ... and has a /32 and /128 applied
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32, bind=b_ip4
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 128, bind=b_ip6
+ ).add_vpp_config()
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd2.bvi, "10.0.1.128", 32
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd2.bvi, "2001:11::128", 128
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-groups in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 440,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_220.add_vpp_config()
+ epg_221 = VppGbpEndpointGroup(
+ self,
+ 221,
+ 441,
+ rd1,
+ gbd2,
+ None,
+ gbd2.bvi,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_221.add_vpp_config()
+ epg_222 = VppGbpEndpointGroup(
+ self,
+ 222,
+ 442,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.2.128",
+ "2001:12::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_222.add_vpp_config()
+
+ #
+ # a GBP bridge domains for the SEPs
+ #
+ bd_uu1 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, self.pg7.remote_ip4, 116)
+ bd_uu1.add_vpp_config()
+ bd_uu2 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, self.pg7.remote_ip4, 117)
+ bd_uu2.add_vpp_config()
+
+ bd3 = VppBridgeDomain(self, 3)
+ bd3.add_vpp_config()
+ gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2, bd_uu1, learn=False)
+ gbd3.add_vpp_config()
+ bd4 = VppBridgeDomain(self, 4)
+ bd4.add_vpp_config()
+ gbd4 = VppGbpBridgeDomain(self, bd4, rd1, self.loop3, bd_uu2, learn=False)
+ gbd4.add_vpp_config()
+
+ #
+ # EPGs in which the service endpoints exist
+ #
+ epg_320 = VppGbpEndpointGroup(
+ self,
+ 320,
+ 550,
+ rd1,
+ gbd3,
+ None,
+ gbd1.bvi,
+ "12.0.0.128",
+ "4001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_320.add_vpp_config()
+ epg_321 = VppGbpEndpointGroup(
+ self,
+ 321,
+ 551,
+ rd1,
+ gbd4,
+ None,
+ gbd2.bvi,
+ "12.0.1.128",
+ "4001:11::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_321.add_vpp_config()
+
+ #
+ # three local endpoints
+ #
+ ep1 = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001:10::1",
+ )
+ ep1.add_vpp_config()
+ ep2 = VppGbpEndpoint(
+ self,
+ self.pg1,
+ epg_221,
+ None,
+ "10.0.1.1",
+ "11.0.1.1",
+ "2001:11::1",
+ "3001:11::1",
+ )
+ ep2.add_vpp_config()
+ ep3 = VppGbpEndpoint(
+ self,
+ self.pg2,
+ epg_222,
+ None,
+ "10.0.2.2",
+ "11.0.2.2",
+ "2001:12::1",
+ "3001:12::1",
+ )
+ ep3.add_vpp_config()
+
+ #
+ # service endpoints
+ #
+ sep1 = VppGbpEndpoint(
+ self,
+ self.pg3,
+ epg_320,
+ None,
+ "12.0.0.1",
+ "13.0.0.1",
+ "4001:10::1",
+ "5001:10::1",
+ )
+ sep1.add_vpp_config()
+ sep2 = VppGbpEndpoint(
+ self,
+ self.pg4,
+ epg_320,
+ None,
+ "12.0.0.2",
+ "13.0.0.2",
+ "4001:10::2",
+ "5001:10::2",
+ )
+ sep2.add_vpp_config()
+ sep3 = VppGbpEndpoint(
+ self,
+ self.pg5,
+ epg_321,
+ None,
+ "12.0.1.1",
+ "13.0.1.1",
+ "4001:11::1",
+ "5001:11::1",
+ )
+ sep3.add_vpp_config()
+ # this EP is not installed immediately
+ sep4 = VppGbpEndpoint(
+ self,
+ self.pg6,
+ epg_321,
+ None,
+ "12.0.1.2",
+ "13.0.1.2",
+ "4001:11::2",
+ "5001:11::2",
+ )
+
+ #
+ # an L2 switch packet between local EPs in different EPGs
+ # different dest ports on each so the are LB hashed differently
+ #
+ p4 = [
+ (
+ Ether(src=ep1.mac, dst=ep3.mac)
+ / IP(src=ep1.ip4, dst=ep3.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep3.mac, dst=ep1.mac)
+ / IP(src=ep3.ip4, dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=ep3.mac)
+ / IPv6(src=ep1.ip6, dst=ep3.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep3.mac, dst=ep1.mac)
+ / IPv6(src=ep3.ip6, dst=ep1.ip6)
+ / UDP(sport=1234, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # should be dropped since no contract yet
+ self.send_and_assert_no_replies(self.pg0, [p4[0]])
+ self.send_and_assert_no_replies(self.pg0, [p6[0]])
+
+ #
+ # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
+ # one of the next-hops is via an EP that is not known
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # test the src-ip hash mode
+ #
+ c1 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_222.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 402,
+ epg_222.sclass,
+ epg_220.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ #
+ # send again with the contract preset, now packets arrive
+ # at SEP1 or SEP2 depending on the hashing
+ #
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep3.ip4)
+
+ rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep2.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep2.mac)
+ self.assertEqual(rx[IP].src, ep3.ip4)
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 117)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep4.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep3.ip6)
+
+ rxs = self.send_and_expect(self.pg2, p6[1] * 17, sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, ep3.ip6)
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ #
+ # programme the unknown EP
+ #
+ sep4.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep4.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, ep3.ip6)
+
+ #
+ # and revert back to unprogrammed
+ #
+ sep4.remove_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 117)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep4.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep3.ip6)
+
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+
+ #
+ # test the symmetric hash mode
+ #
+ c1 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_222.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 402,
+ epg_222.sclass,
+ epg_220.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ #
+ # send again with the contract preset, now packets arrive
+ # at SEP1 for both directions
+ #
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep3.ip4)
+
+ rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep3.ip4)
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ #
+ # programme the unknown EP for the L3 tests
+ #
+ sep4.add_vpp_config()
+
+ #
+ # an L3 switch packet between local EPs in different EPGs
+ # different dest ports on each so the are LB hashed differently
+ #
+ p4 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IP(src=ep2.ip4, dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IPv6(src=ep1.ip6, dst=ep2.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IPv6(src=ep2.ip6, dst=ep1.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ c3 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_221.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ #
+ # learn a remote EP in EPG 221
+ # packets coming from unknown remote EPs will be leant & redirected
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self,
+ 444,
+ rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l3.add_vpp_config()
+
+ c4 = VppGbpContract(
+ self,
+ 402,
+ epg_221.sclass,
+ epg_220.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c4.add_vpp_config()
+
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=444, gpid=441, flags=0x88)
+ / Ether(src="00:22:22:22:22:33", dst=str(self.router_mac))
+ / IP(src="10.0.0.88", dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ # unknown remote EP to local EP redirected
+ rxs = self.send_and_expect(self.pg7, [p], sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, "10.0.0.88")
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="10.0.0.88"))
+
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=444, gpid=441, flags=0x88)
+ / Ether(src="00:22:22:22:22:33", dst=str(self.router_mac))
+ / IPv6(src="2001:10::88", dst=ep1.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ # unknown remote EP to local EP redirected (ipv6)
+ rxs = self.send_and_expect(self.pg7, [p], sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, "2001:10::88")
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="2001:10::88")
+ )
+
+ #
+ # L3 switch from local to remote EP
+ #
+ p4 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IP(src=ep1.ip4, dst="10.0.0.88")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IPv6(src=ep1.ip6, dst="2001:10::88")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, "10.0.0.88")
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep4.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, "2001:10::88")
+
+ #
+ # test the dst-ip hash mode
+ #
+ c5 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_221.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep2.vmac, sep2.epg.bd, sep2.ip4, sep2.epg.rd
+ ),
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep3.vmac, sep3.epg.bd, sep3.ip6, sep3.epg.rd
+ ),
+ VppGbpContractNextHop(
+ sep4.vmac, sep4.epg.bd, sep4.ip6, sep4.epg.rd
+ ),
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c5.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, "10.0.0.88")
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, "2001:10::88")
+
+ #
+ # a programmed remote SEP in EPG 320
+ #
+
+ # gbp vxlan tunnel for the remote SEP
+ vx_tun_l3_sep = VppGbpVxlanTunnel(
+ self,
+ 555,
+ rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l3_sep.add_vpp_config()
+
+ # remote SEP
+ sep5 = VppGbpEndpoint(
+ self,
+ vx_tun_l3_sep,
+ epg_320,
+ None,
+ "12.0.0.10",
+ "13.0.0.10",
+ "4001:10::10",
+ "5001:10::10",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ mac=None,
+ )
+ sep5.add_vpp_config()
+
+ #
+ # local l3out redirect tests
+ #
+
+ # add local l3out
+ # the external bd
+ self.loop4.set_mac(self.router_mac)
+ b_lo4_ip4 = VppIpInterfaceBind(self, self.loop4, t4).add_vpp_config()
+ b_lo4_ip6 = VppIpInterfaceBind(self, self.loop4, t6).add_vpp_config()
+ ebd = VppBridgeDomain(self, 100)
+ ebd.add_vpp_config()
+ gebd = VppGbpBridgeDomain(self, ebd, rd1, self.loop4, None, None)
+ gebd.add_vpp_config()
+ # the external epg
+ eepg = VppGbpEndpointGroup(
+ self,
+ 888,
+ 765,
+ rd1,
+ gebd,
+ None,
+ gebd.bvi,
+ "10.1.0.128",
+ "2001:10:1::128",
+ VppGbpEndpointRetention(60),
+ )
+ eepg.add_vpp_config()
+ # add subnets to BVI
+ VppIpInterfaceAddress(
+ self, gebd.bvi, "10.1.0.128", 24, bind=b_lo4_ip4
+ ).add_vpp_config()
+ VppIpInterfaceAddress(
+ self, gebd.bvi, "2001:10:1::128", 64, bind=b_lo4_ip6
+ ).add_vpp_config()
+ # ... which are L3-out subnets
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.1.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=765,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "2001:10:1::128",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=765,
+ ).add_vpp_config()
+ # external endpoints
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ eep1 = VppGbpEndpoint(
+ self,
+ self.vlan_100,
+ eepg,
+ None,
+ "10.1.0.1",
+ "11.1.0.1",
+ "2001:10:1::1",
+ "3001:10:1::1",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep1.add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ eep2 = VppGbpEndpoint(
+ self,
+ self.vlan_101,
+ eepg,
+ None,
+ "10.1.0.2",
+ "11.1.0.2",
+ "2001:10:1::2",
+ "3001:10:1::2",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep2.add_vpp_config()
+
+ # external subnets reachable though eep1 and eep2 respectively
+ VppIpRoute(
+ self,
+ "10.220.0.0",
+ 24,
+ [VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.220.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ ).add_vpp_config()
+ VppIpRoute(
+ self,
+ "10:220::",
+ 64,
+ [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)],
+ table_id=t6.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10:220::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ ).add_vpp_config()
+ VppIpRoute(
+ self,
+ "10.221.0.0",
+ 24,
+ [VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.221.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221,
+ ).add_vpp_config()
+ VppIpRoute(
+ self,
+ "10:221::",
+ 64,
+ [VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index)],
+ table_id=t6.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10:221::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221,
+ ).add_vpp_config()
+
+ #
+ # l3out redirect to remote (known, then unknown) SEP
+ #
+
+ # packets from 1 external subnet to the other
+ p = [
+ (
+ Ether(src=eep1.mac, dst=self.router_mac)
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.17", dst="10.221.0.65")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=eep1.mac, dst=self.router_mac)
+ / Dot1Q(vlan=100)
+ / IPv6(src="10:220::17", dst="10:221::65")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # packets should be dropped in absence of contract
+ self.send_and_assert_no_replies(self.pg0, p)
+
+ # contract redirecting to sep5
+ VppGbpContract(
+ self,
+ 402,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip4, sep5.epg.rd
+ )
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip6, sep5.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ ).add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the programmed remote leaf TEP
+ self.assertEqual(rx[VXLAN].vni, 555)
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[Dot1Q].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ # remote SEP: it is now an unknown remote SEP and should go
+ # to spine proxy
+ sep5.remove_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the spine proxy TEP
+ self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[Dot1Q].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # l3out redirect to local SEP
+ #
+
+ # change the contract between l3out to redirect to local SEPs
+ # instead of remote SEP
+ VppGbpContract(
+ self,
+ 402,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ )
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip6, sep1.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ ).add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, sep1.itf)
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ rxip = rx[Ether].payload
+ txip = tx[Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # redirect remote EP to remote (known then unknown) SEP
+ #
+
+ # remote SEP known again
+ sep5.add_vpp_config()
+
+ # contract to redirect to learnt SEP
+ VppGbpContract(
+ self,
+ 402,
+ epg_221.sclass,
+ epg_222.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip4, sep5.epg.rd
+ )
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip6, sep5.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ ).add_vpp_config()
+
+ # packets from unknown EP 221 to known EP in EPG 222
+ # should be redirected to known remote SEP
+ base = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=444, gpid=441, flags=0x88)
+ / Ether(src="00:22:22:22:22:44", dst=str(self.router_mac))
+ )
+ p = [
+ (
+ base
+ / IP(src="10.0.1.100", dst=ep3.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ base
+ / IPv6(src="2001:10::100", dst=ep3.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # unknown remote EP to local EP redirected to known remote SEP
+ rxs = self.send_and_expect(self.pg7, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the programmed remote leaf TEP
+ self.assertEqual(rx[VXLAN].vni, 555)
+ self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[VXLAN][Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="10.0.1.100")
+ )
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="2001:10::100")
+ )
+
+ # remote SEP: it is now an unknown remote SEP and should go
+ # to spine proxy
+ sep5.remove_vpp_config()
+
+ # remote EP (coming from spine proxy) to local EP redirected to
+ # known remote SEP
+ rxs = self.send_and_expect(self.pg7, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the spine proxy TEP
+ self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
+ self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[VXLAN][Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # cleanup
+ #
+ self.pg7.unconfig_ip4()
+
+ def test_gbp_redirect_extended(self):
+ """GBP Endpoint Redirect Extended"""
+
+ self.vapi.cli("set logging class gbp level debug")
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ learnt = [
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.1.2", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:03", "ip": "10.0.1.3", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ # create IPv4 and IPv6 RD UU VxLAN-GBP TEP and bind them to the right
+ # VRF
+ rd_uu4 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 114,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ rd_uu4.add_vpp_config()
+ VppIpInterfaceBind(self, rd_uu4, t4).add_vpp_config()
+
+ rd_uu6 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 115,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ rd_uu6.add_vpp_config()
+ VppIpInterfaceBind(self, rd_uu6, t4).add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6, rd_uu4, rd_uu6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+ self.loop1.set_mac(self.router_mac)
+ self.loop2.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_lo0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_lo0_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+ b_lo1_ip4 = VppIpInterfaceBind(self, self.loop1, t4).add_vpp_config()
+ b_lo1_ip6 = VppIpInterfaceBind(self, self.loop1, t6).add_vpp_config()
+ b_lo2_ip4 = VppIpInterfaceBind(self, self.loop2, t4).add_vpp_config()
+ b_lo2_ip6 = VppIpInterfaceBind(self, self.loop2, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's UU-fwd
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a GBP bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
+ gbd1.add_vpp_config()
+
+ bd2 = VppBridgeDomain(self, 2)
+ bd2.add_vpp_config()
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
+ gbd2.add_vpp_config()
+
+ # ... and has a /32 and /128 applied
+ ip4_addr1 = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32, bind=b_lo0_ip4
+ ).add_vpp_config()
+ ip6_addr1 = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 128, bind=b_lo0_ip6
+ ).add_vpp_config()
+ ip4_addr2 = VppIpInterfaceAddress(
+ self, gbd2.bvi, "10.0.1.128", 32, bind=b_lo1_ip4
+ ).add_vpp_config()
+ ip6_addr2 = VppIpInterfaceAddress(
+ self, gbd2.bvi, "2001:11::128", 128, bind=b_lo1_ip6
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-groups
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 440,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_220.add_vpp_config()
+ epg_221 = VppGbpEndpointGroup(
+ self,
+ 221,
+ 441,
+ rd1,
+ gbd2,
+ None,
+ gbd2.bvi,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_221.add_vpp_config()
+
+ #
+ # a GBP bridge domains for the SEPs
+ #
+ bd_uu3 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, self.pg7.remote_ip4, 116)
+ bd_uu3.add_vpp_config()
+
+ bd3 = VppBridgeDomain(self, 3)
+ bd3.add_vpp_config()
+ gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2, bd_uu3, learn=False)
+ gbd3.add_vpp_config()
+
+ ip4_addr3 = VppIpInterfaceAddress(
+ self, gbd3.bvi, "12.0.0.128", 32, bind=b_lo2_ip4
+ ).add_vpp_config()
+ ip6_addr3 = VppIpInterfaceAddress(
+ self, gbd3.bvi, "4001:10::128", 128, bind=b_lo2_ip6
+ ).add_vpp_config()
+
+ #
+ # self.logger.info(self.vapi.cli("show gbp bridge"))
+ # self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+ # self.logger.info(self.vapi.cli("show gbp vxlan"))
+ # self.logger.info(self.vapi.cli("show int addr"))
+ #
+
+ #
+ # EPGs in which the service endpoints exist
+ #
+ epg_320 = VppGbpEndpointGroup(
+ self,
+ 320,
+ 550,
+ rd1,
+ gbd3,
+ None,
+ gbd3.bvi,
+ "12.0.0.128",
+ "4001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_320.add_vpp_config()
+
+ #
+ # endpoints
+ #
+ ep1 = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001:10::1",
+ )
+ ep1.add_vpp_config()
+ ep2 = VppGbpEndpoint(
+ self,
+ self.pg1,
+ epg_221,
+ None,
+ "10.0.1.1",
+ "11.0.1.1",
+ "2001:11::1",
+ "3001:11::1",
+ )
+ ep2.add_vpp_config()
+
+ #
+ # service endpoints
+ #
+ sep1 = VppGbpEndpoint(
+ self,
+ self.pg3,
+ epg_320,
+ None,
+ "12.0.0.1",
+ "13.0.0.1",
+ "4001:10::1",
+ "5001:10::1",
+ )
+ sep2 = VppGbpEndpoint(
+ self,
+ self.pg4,
+ epg_320,
+ None,
+ "12.0.0.2",
+ "13.0.0.2",
+ "4001:10::2",
+ "5001:10::2",
+ )
+
+ # sep1 and sep2 are not added to config yet
+ # they are unknown for now
+
+ #
+ # add routes to EPG subnets
+ #
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.1.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ ).add_vpp_config()
+
+ #
+ # Local host to known local host in different BD
+ # with SFC contract (source and destination are in
+ # one node and service endpoint in another node)
+ #
+ p4 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IP(src=ep2.ip4, dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IPv6(src=ep1.ip6, dst=ep2.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IPv6(src=ep2.ip6, dst=ep1.ip6)
+ / UDP(sport=1234, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # should be dropped since no contract yet
+ self.send_and_assert_no_replies(self.pg0, [p4[0]])
+ self.send_and_assert_no_replies(self.pg0, [p6[0]])
+
+ #
+ # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
+ # one of the next-hops is via an EP that is not known
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # test the src-ip hash mode
+ #
+ c1 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_221.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ )
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip6, sep1.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 402,
+ epg_221.sclass,
+ epg_220.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.epg.rd
+ )
+ ],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip6, sep1.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ # ep1 <--> ep2 redirected through sep1
+ # sep1 is unknown
+ # packet is redirected to sep bd and then go through sep bd UU
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IP].src, ep1.ip4)
+ self.assertEqual(inner[IP].dst, ep2.ip4)
+
+ rxs = self.send_and_expect(self.pg1, p4[1] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IP].src, ep2.ip4)
+ self.assertEqual(inner[IP].dst, ep1.ip4)
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep2.ip6)
+
+ rxs = self.send_and_expect(self.pg1, p6[1] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IPv6].src, ep2.ip6)
+ self.assertEqual(inner[IPv6].dst, ep1.ip6)
+
+ # configure sep1: it is now local
+ # packets between ep1 and ep2 are redirected locally
+ sep1.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ rxs = self.send_and_expect(self.pg1, p6[1] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IPv6].src, ep2.ip6)
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ # packet coming from the l2 spine-proxy to sep1
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=116, gpid=440, gpflags=0x08, flags=0x88)
+ / Ether(src=str(self.router_mac), dst=sep1.mac)
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, [p] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ # contract for SEP to communicate with dst EP
+ c3 = VppGbpContract(
+ self,
+ 402,
+ epg_320.sclass,
+ epg_221.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ # temporarily remove ep2, so that ep2 is remote & unknown
+ ep2.remove_vpp_config()
+
+ # packet going back from sep1 to its original dest (ep2)
+ # as ep2 is now unknown (see above), it must go through
+ # the rd UU (packet is routed)
+
+ p1 = (
+ Ether(src=sep1.mac, dst=self.router_mac)
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg3, [p1] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 114)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep1.ip4)
+ self.assertEqual(inner[IP].dst, ep2.ip4)
+
+ self.logger.info(self.vapi.cli("show bridge 3 detail"))
+ sep1.remove_vpp_config()
+
+ self.logger.info(self.vapi.cli("show bridge 1 detail"))
+ self.logger.info(self.vapi.cli("show bridge 2 detail"))
+
+ # re-add ep2: it is local again :)
+ ep2.add_vpp_config()
+
+ # packet coming back from the remote sep through rd UU
+ p2 = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=114, gpid=441, gpflags=0x09, flags=0x88)
+ / Ether(src=str(self.router_mac), dst=self.router_mac)
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, [p2], self.pg1)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ #
+ # bd_uu2.add_vpp_config()
+ #
+
+ #
+ # cleanup
+ #
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+ c3.remove_vpp_config()
+ self.pg7.unconfig_ip4()
+
+ def test_gbp_l3_out(self):
+ """GBP L3 Out"""
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ self.vapi.cli("set logging class gbp level debug")
+
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's BUM
+ # Pg1 some other l3 interface
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a multicast vxlan-gbp tunnel for broadcast in the BD
+ #
+ tun_bm = VppVxlanGbpTunnel(
+ self, self.pg7.local_ip4, "239.1.1.1", 88, mcast_itf=self.pg7
+ )
+ tun_bm.add_vpp_config()
+
+ #
+ # a GBP external bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, tun_bm)
+ gbd1.add_vpp_config()
+
+ #
+ # The Endpoint-groups in which the external endpoints exist
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 113,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+
+ # the BVIs have the subnets applied ...
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 24, bind=b_ip4
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 64, bind=b_ip6
+ ).add_vpp_config()
+
+ # ... which are L3-out subnets
+ l3o_1 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=113,
+ )
+ l3o_1.add_vpp_config()
+
+ #
+ # an external interface attached to the outside world and the
+ # external BD
+ #
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ vlan_144 = VppDot1QSubint(self, self.pg0, 144)
+ vlan_144.admin_up()
+ # vlan_102 is not poped
+
+ #
+ # an unicast vxlan-gbp for inter-RD traffic
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self,
+ 444,
+ rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l3.add_vpp_config()
+
+ #
+ # External Endpoints
+ #
+ eep1 = VppGbpEndpoint(
+ self,
+ self.vlan_100,
+ epg_220,
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001::1",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep1.add_vpp_config()
+ eep2 = VppGbpEndpoint(
+ self,
+ self.vlan_101,
+ epg_220,
+ None,
+ "10.0.0.2",
+ "11.0.0.2",
+ "2001:10::2",
+ "3001::2",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep2.add_vpp_config()
+ eep3 = VppGbpEndpoint(
+ self,
+ self.vlan_102,
+ epg_220,
+ None,
+ "10.0.0.3",
+ "11.0.0.3",
+ "2001:10::3",
+ "3001::3",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep3.add_vpp_config()
+
+ #
+ # A remote external endpoint
+ #
+ rep = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.0.101",
+ "11.0.0.101",
+ "2001:10::101",
+ "3001::101",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ mac=None,
+ )
+ rep.add_vpp_config()
+
+ #
+ # EP1 impersonating EP3 is dropped
+ #
+ p = (
+ Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=100)
+ / ARP(
+ op="who-has",
+ psrc="10.0.0.3",
+ pdst="10.0.0.128",
+ hwsrc=eep1.mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ self.send_and_assert_no_replies(self.pg0, p)
+
+ #
+ # ARP packet from External EPs are accepted and replied to
+ #
+ p_arp = (
+ Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=100)
+ / ARP(
+ op="who-has",
+ psrc=eep1.ip4,
+ pdst="10.0.0.128",
+ hwsrc=eep1.mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
+
+ #
+ # ARP packet from host in remote subnet are accepted and replied to
+ #
+ p_arp = (
+ Ether(src=eep3.mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=102)
+ / ARP(
+ op="who-has",
+ psrc=eep3.ip4,
+ pdst="10.0.0.128",
+ hwsrc=eep3.mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
+
+ #
+ # packets destined to unknown addresses in the BVI's subnet
+ # are ARP'd for
+ #
+ p4 = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.0.0.1", dst="10.0.0.88")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ p6 = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IPv6(src="2001:10::1", dst="2001:10::88")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, "239.1.1.1")
+ self.assertEqual(rx[VXLAN].vni, 88)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # policy was applied to the original IP packet
+ self.assertEqual(rx[VXLAN].gpid, 113)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertTrue(inner.haslayer(ARP))
+
+ #
+ # remote to external
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=444, gpid=113, flags=0x88)
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src="10.0.0.101", dst="10.0.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # local EP pings router
+ #
+ p = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src=eep1.ip4, dst="10.0.0.128")
+ / ICMP(type="echo-request")
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, eep1.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # local EP pings other local EP
+ #
+ p = (
+ Ether(src=eep1.mac, dst=eep2.mac)
+ / Dot1Q(vlan=100)
+ / IP(src=eep1.ip4, dst=eep2.ip4)
+ / ICMP(type="echo-request")
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, eep1.mac)
+ self.assertEqual(rx[Ether].dst, eep2.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ #
+ # local EP pings router w/o vlan tag poped
+ #
+ p = (
+ Ether(src=eep3.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=102)
+ / IP(src=eep3.ip4, dst="10.0.0.128")
+ / ICMP(type="echo-request")
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.vlan_102.remote_mac)
+
+ #
+ # A ip4 subnet reachable through the external EP1
+ #
+ ip_220 = VppIpRoute(
+ self,
+ "10.220.0.0",
+ 24,
+ [VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_220.add_vpp_config()
+
+ l3o_220 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.220.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o_220.add_vpp_config()
+
+ #
+ # An ip6 subnet reachable through the external EP1
+ #
+ ip6_220 = VppIpRoute(
+ self,
+ "10:220::",
+ 64,
+ [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)],
+ table_id=t6.table_id,
+ )
+ ip6_220.add_vpp_config()
+
+ l3o6_220 = VppGbpSubnet(
+ self,
+ rd1,
+ "10:220::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o6_220.add_vpp_config()
+
+ #
+ # A subnet reachable through the external EP2
+ #
+ ip_221 = VppIpRoute(
+ self,
+ "10.221.0.0",
+ 24,
+ [VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_221.add_vpp_config()
+
+ l3o_221 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.221.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221,
+ )
+ l3o_221.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ # dropped without a contract
+ #
+ p = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.221.0.1")
+ / ICMP(type="echo-request")
+ )
+
+ self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # contract for the external nets to communicate
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # A contract with the wrong scope is not matched
+ #
+ c_44 = VppGbpContract(
+ self,
+ 44,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c_44.add_vpp_config()
+ self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ c1 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ #
+ # Contracts allowing ext-net 200 to talk with external EPs
+ #
+ c2 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 113,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+ c3 = VppGbpContract(
+ self,
+ 55,
+ 113,
+ 4220,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ #
+ p = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.221.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, eep2.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ # we did not learn these external hosts
+ self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1"))
+ self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1"))
+
+ #
+ # from remote external EP to local external EP
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=444, gpid=113, flags=0x88)
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src="10.0.0.101", dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # ping from an external host to the remote external EP
+ #
+ p = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst=rep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 444)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # the sclass of the ext-net the packet came from
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ # policy was applied to the original IP packet
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ # since it's an external host the reciever should not learn it
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[IP].src, "10.220.0.1")
+ self.assertEqual(inner[IP].dst, rep.ip4)
+
+ #
+ # An external subnet reachable via the remote external EP
+ #
+
+ #
+ # first the VXLAN-GBP tunnel over which it is reached
+ #
+ vx_tun_r1 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 445,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ vx_tun_r1.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r1, t4).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+ #
+ # then the special adj to resolve through on that tunnel
+ #
+ n1 = VppNeighbor(
+ self, vx_tun_r1.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip4
+ )
+ n1.add_vpp_config()
+
+ #
+ # the route via the adj above
+ #
+ ip_222 = VppIpRoute(
+ self,
+ "10.222.0.0",
+ 24,
+ [VppRoutePath(self.pg7.remote_ip4, vx_tun_r1.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_222.add_vpp_config()
+
+ l3o_222 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.222.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4222,
+ )
+ l3o_222.add_vpp_config()
+
+ #
+ # ping between hosts in local and remote external subnets
+ # dropped without a contract
+ #
+ p = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.222.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # Add contracts ext-nets for 220 -> 222
+ #
+ c4 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4222,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c4.add_vpp_config()
+
+ #
+ # ping from host in local to remote external subnets
+ #
+ p = (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.222.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 3, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 445)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # the sclass of the ext-net the packet came from
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ # policy was applied to the original IP packet
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ # since it's an external host the reciever should not learn it
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c")
+ self.assertEqual(inner[IP].src, "10.220.0.1")
+ self.assertEqual(inner[IP].dst, "10.222.0.1")
+
+ #
+ # make the external subnet ECMP
+ #
+ vx_tun_r2 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 446,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ vx_tun_r2.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r2, t4).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+ n2 = VppNeighbor(
+ self, vx_tun_r2.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip4
+ )
+ n2.add_vpp_config()
+
+ ip_222.modify(
+ [
+ VppRoutePath(self.pg7.remote_ip4, vx_tun_r1.sw_if_index),
+ VppRoutePath(self.pg7.remote_ip4, vx_tun_r2.sw_if_index),
+ ]
+ )
+
+ #
+ # now expect load-balance
+ #
+ p = [
+ (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.222.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.222.0.1")
+ / UDP(sport=1222, dport=1235)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ self.assertEqual(rxs[0][VXLAN].vni, 445)
+ self.assertEqual(rxs[1][VXLAN].vni, 446)
+
+ #
+ # Same LB test for v6
+ #
+ n3 = VppNeighbor(
+ self, vx_tun_r1.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip6
+ )
+ n3.add_vpp_config()
+ n4 = VppNeighbor(
+ self, vx_tun_r2.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip6
+ )
+ n4.add_vpp_config()
+
+ ip_222_6 = VppIpRoute(
+ self,
+ "10:222::",
+ 64,
+ [
+ VppRoutePath(self.pg7.remote_ip6, vx_tun_r1.sw_if_index),
+ VppRoutePath(self.pg7.remote_ip6, vx_tun_r2.sw_if_index),
+ ],
+ table_id=t6.table_id,
+ )
+ ip_222_6.add_vpp_config()
+
+ l3o_222_6 = VppGbpSubnet(
+ self,
+ rd1,
+ "10:222::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4222,
+ )
+ l3o_222_6.add_vpp_config()
+
+ p = [
+ (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IPv6(src="10:220::1", dst="10:222::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IPv6(src="10:220::1", dst="10:222::1")
+ / UDP(sport=7777, dport=8881)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ self.logger.info(self.vapi.cli("sh ip6 fib 10:222::1"))
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ self.assertEqual(rxs[0][VXLAN].vni, 445)
+ self.assertEqual(rxs[1][VXLAN].vni, 446)
+
+ #
+ # ping from host in remote to local external subnets
+ # there's no contract for this, but the A bit is set.
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=445, gpid=4222, flags=0x88, gpflags="A")
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src="10.222.0.1", dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
+ self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
+
+ #
+ # ping from host in remote to remote external subnets
+ # this is dropped by reflection check.
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=445, gpid=4222, flags=0x88, gpflags="A")
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src="10.222.0.1", dst="10.222.0.2")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
+
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=445, gpid=4222, flags=0x88, gpflags="A")
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src="10:222::1", dst="10:222::2")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
+
+ #
+ # local EP
+ #
+ lep1 = VppGbpEndpoint(
+ self,
+ vlan_144,
+ epg_220,
+ None,
+ "10.0.0.44",
+ "11.0.0.44",
+ "2001:10::44",
+ "3001::44",
+ )
+ lep1.add_vpp_config()
+
+ #
+ # local EP to local ip4 external subnet
+ #
+ p = (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IP(src=lep1.ip4, dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, eep1.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # local EP to local ip6 external subnet
+ #
+ p = (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IPv6(src=lep1.ip6, dst="10:220::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, eep1.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # ip4 and ip6 subnets that load-balance
+ #
+ ip_20 = VppIpRoute(
+ self,
+ "10.20.0.0",
+ 24,
+ [
+ VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index),
+ VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index),
+ ],
+ table_id=t4.table_id,
+ )
+ ip_20.add_vpp_config()
+
+ l3o_20 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.20.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o_20.add_vpp_config()
+
+ ip6_20 = VppIpRoute(
+ self,
+ "10:20::",
+ 64,
+ [
+ VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index),
+ VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index),
+ ],
+ table_id=t6.table_id,
+ )
+ ip6_20.add_vpp_config()
+
+ l3o6_20 = VppGbpSubnet(
+ self,
+ rd1,
+ "10:20::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o6_20.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh ip fib 10.20.0.1"))
+ self.logger.info(self.vapi.cli("sh ip6 fib 10:20::1"))
+
+ # two ip6 packets whose port are chosen so they load-balance
+ p = [
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IPv6(src=lep1.ip6, dst="10:20::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IPv6(src=lep1.ip6, dst="10:20::1")
+ / UDP(sport=124, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
+
+ self.assertEqual(rxs[0][Dot1Q].vlan, 101)
+ self.assertEqual(rxs[1][Dot1Q].vlan, 100)
+
+ # two ip4 packets whose port are chosen so they load-balance
+ p = [
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IP(src=lep1.ip4, dst="10.20.0.1")
+ / UDP(sport=1235, dport=1235)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IP(src=lep1.ip4, dst="10.20.0.1")
+ / UDP(sport=124, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
+
+ self.assertEqual(rxs[0][Dot1Q].vlan, 101)
+ self.assertEqual(rxs[1][Dot1Q].vlan, 100)
+
+ #
+ # cleanup
+ #
+ ip_222.remove_vpp_config()
+ self.pg7.unconfig_ip4()
+ self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED)
+ self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
+
+ def test_gbp_anon_l3_out(self):
+ """GBP Anonymous L3 Out"""
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ self.vapi.cli("set logging class gbp level debug")
+
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ bind_l0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ bind_l0_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's BUM
+ # Pg1 some other l3 interface
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a GBP external bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, None)
+ gbd1.add_vpp_config()
+
+ #
+ # The Endpoint-groups in which the external endpoints exist
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 113,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+
+ # the BVIs have the subnet applied ...
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 24, bind=bind_l0_ip4
+ ).add_vpp_config()
+
+ # ... which is an Anonymous L3-out subnets
+ l3o_1 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_ANON_L3_OUT,
+ sclass=113,
+ )
+ l3o_1.add_vpp_config()
+
+ #
+ # an external interface attached to the outside world and the
+ # external BD
+ #
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+
+ #
+ # vlan_100 and vlan_101 are anonymous l3-out interfaces
+ #
+ ext_itf = VppGbpExtItf(self, self.vlan_100, bd1, rd1, anon=True)
+ ext_itf.add_vpp_config()
+ ext_itf = VppGbpExtItf(self, self.vlan_101, bd1, rd1, anon=True)
+ ext_itf.add_vpp_config()
+
+ #
+ # an unicast vxlan-gbp for inter-RD traffic
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self,
+ 444,
+ rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l3.add_vpp_config()
+
+ #
+ # A remote external endpoint
+ #
+ rep = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.0.201",
+ "11.0.0.201",
+ "2001:10::201",
+ "3001::101",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ mac=None,
+ )
+ rep.add_vpp_config()
+
+ #
+ # ARP packet from host in external subnet are accepted, flooded and
+ # replied to. We expect 2 packets:
+ # - APR request flooded over the other vlan subif
+ # - ARP reply from BVI
+ #
+ p_arp = (
+ Ether(src=self.vlan_100.remote_mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=100)
+ / ARP(
+ op="who-has",
+ psrc="10.0.0.100",
+ pdst="10.0.0.128",
+ hwsrc=self.vlan_100.remote_mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2)
+
+ p_arp = (
+ Ether(src=self.vlan_101.remote_mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=101)
+ / ARP(
+ op="who-has",
+ psrc="10.0.0.101",
+ pdst="10.0.0.128",
+ hwsrc=self.vlan_101.remote_mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2)
+
+ #
+ # remote to external
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=vx_tun_l3.vni, gpid=epg_220.sclass, flags=0x88)
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=str(rep.ip4), dst="10.0.0.100")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # local EP pings router
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.0.0.100", dst="10.0.0.128")
+ / ICMP(type="echo-request")
+ )
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.vlan_100.remote_mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # local EP pings other local EP
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=self.vlan_101.remote_mac)
+ / Dot1Q(vlan=100)
+ / IP(src="10.0.0.100", dst="10.0.0.101")
+ / ICMP(type="echo-request")
+ )
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.vlan_100.remote_mac)
+ self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ #
+ # A subnet reachable through an external router on vlan 100
+ #
+ ip_220 = VppIpRoute(
+ self,
+ "10.220.0.0",
+ 24,
+ [VppRoutePath("10.0.0.100", epg_220.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_220.add_vpp_config()
+
+ l3o_220 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.220.0.0",
+ 24,
+ # note: this a "regular" L3 out subnet (not connected)
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o_220.add_vpp_config()
+
+ #
+ # A subnet reachable through an external router on vlan 101
+ #
+ ip_221 = VppIpRoute(
+ self,
+ "10.221.0.0",
+ 24,
+ [VppRoutePath("10.0.0.101", epg_220.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_221.add_vpp_config()
+
+ l3o_221 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.221.0.0",
+ 24,
+ # note: this a "regular" L3 out subnet (not connected)
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221,
+ )
+ l3o_221.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ # dropped without a contract
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.221.0.1")
+ / ICMP(type="echo-request")
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # contract for the external nets to communicate
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ #
+ # Contracts allowing ext-net 200 to talk with external EPs
+ #
+ c2 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 113,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+ c3 = VppGbpContract(
+ self,
+ 55,
+ 113,
+ 4220,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.221.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ # we did not learn these external hosts
+ self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1"))
+ self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1"))
+
+ #
+ # from remote external EP to local external EP
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=444, gpid=113, flags=0x88)
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=rep.ip4, dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # ping from an external host to the remote external EP
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst=rep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 444)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # the sclass of the ext-net the packet came from
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ # policy was applied to the original IP packet
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ # since it's an external host the reciever should not learn it
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[IP].src, "10.220.0.1")
+ self.assertEqual(inner[IP].dst, rep.ip4)
+
+ #
+ # An external subnet reachable via the remote external EP
+ #
+
+ #
+ # first the VXLAN-GBP tunnel over which it is reached
+ #
+ vx_tun_r = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 445,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ vx_tun_r.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r, t4).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+ #
+ # then the special adj to resolve through on that tunnel
+ #
+ n1 = VppNeighbor(
+ self, vx_tun_r.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip4
+ )
+ n1.add_vpp_config()
+
+ #
+ # the route via the adj above
+ #
+ ip_222 = VppIpRoute(
+ self,
+ "10.222.0.0",
+ 24,
+ [VppRoutePath(self.pg7.remote_ip4, vx_tun_r.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_222.add_vpp_config()
+
+ l3o_222 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.222.0.0",
+ 24,
+ # note: this a "regular" l3out subnet (not connected)
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4222,
+ )
+ l3o_222.add_vpp_config()
+
+ #
+ # ping between hosts in local and remote external subnets
+ # dropped without a contract
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.222.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # Add contracts ext-nets for 220 -> 222
+ #
+ c4 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4222,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c4.add_vpp_config()
+
+ #
+ # ping from host in local to remote external subnets
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst="10.222.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 3, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 445)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # the sclass of the ext-net the packet came from
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ # policy was applied to the original IP packet
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ # since it's an external host the reciever should not learn it
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c")
+ self.assertEqual(inner[IP].src, "10.220.0.1")
+ self.assertEqual(inner[IP].dst, "10.222.0.1")
+
+ #
+ # ping from host in remote to local external subnets
+ # there's no contract for this, but the A bit is set.
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=445, gpid=4222, flags=0x88, gpflags="A")
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src="10.222.0.1", dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
+ self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
+
+ #
+ # ping from host in remote to remote external subnets
+ # this is dropped by reflection check.
+ #
+ p = (
+ Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac)
+ / IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=445, gpid=4222, flags=0x88, gpflags="A")
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src="10.222.0.1", dst="10.222.0.2")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
+
+ #
+ # cleanup
+ #
+ self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED)
+ self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
+ self.pg7.unconfig_ip4()
+ # make sure the programmed EP is no longer learnt from DP
+ self.wait_for_ep_timeout(sw_if_index=rep.itf.sw_if_index, ip=rep.ip4)
+
+
+if __name__ == "__main__":
+ unittest.main(testRunner=VppTestRunner)
diff --git a/extras/deprecated/plugins/l2e/CMakeLists.txt b/extras/deprecated/plugins/l2e/CMakeLists.txt
new file mode 100644
index 00000000000..2bfb05a43e6
--- /dev/null
+++ b/extras/deprecated/plugins/l2e/CMakeLists.txt
@@ -0,0 +1,28 @@
+# 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.
+
+add_vpp_plugin(l2e
+ SOURCES
+ l2e_node.c
+ l2e_api.c
+ l2e.c
+
+ MULTIARCH_SOURCES
+ l2e_node.c
+
+ API_FILES
+ l2e.api
+
+ INSTALL_HEADERS
+ l2e.h
+)
diff --git a/extras/deprecated/netmap/netmap.api b/extras/deprecated/plugins/l2e/l2e.api
index a14753cad9c..586e2bae5ca 100644
--- a/extras/deprecated/netmap/netmap.api
+++ b/extras/deprecated/plugins/l2e/l2e.api
@@ -1,5 +1,6 @@
+/* Hey Emacs use -*- mode: C -*- */
/*
- * Copyright (c) 2015-2016 Cisco and/or its affiliates.
+ * Copyright (c) 2016 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:
@@ -14,39 +15,21 @@
*/
option version = "1.0.0";
+import "vnet/interface_types.api";
-/** \brief Create netmap
+/** \brief L2 emulation at L3
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
- @param netmap_if_name - interface name
- @param hw_addr - interface MAC
- @param use_random_hw_addr - use random generated MAC
- @param is_pipe - is pipe
- @param is_master - 0=slave, 1=master
+ @param sw_if_index - interface the operation is applied to
+ @param enable - Turn the service on or off
*/
-autoreply define netmap_create
+autoreply define l2_emulation
{
+ option status="in_progress";
u32 client_index;
u32 context;
-
- u8 netmap_if_name[64];
- u8 hw_addr[6];
- u8 use_random_hw_addr;
- u8 is_pipe;
- u8 is_master;
-};
-
-/** \brief Delete netmap
- @param client_index - opaque cookie to identify the sender
- @param context - sender context, to match reply w/ request
- @param netmap_if_name - interface name
-*/
-autoreply define netmap_delete
-{
- u32 client_index;
- u32 context;
-
- u8 netmap_if_name[64];
+ vl_api_interface_index_t sw_if_index;
+ bool enable;
};
/*
diff --git a/extras/deprecated/plugins/l2e/l2e.c b/extras/deprecated/plugins/l2e/l2e.c
new file mode 100644
index 00000000000..4c6eac50446
--- /dev/null
+++ b/extras/deprecated/plugins/l2e/l2e.c
@@ -0,0 +1,198 @@
+/*
+ * l2e.c : Extract L3 packets from the L2 input and feed
+ * them into the L3 path.
+ *
+ * Copyright (c) 2013 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 <plugins/l2e/l2e.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/feat_bitmap.h>
+#include <vnet/ip/ip.h>
+
+l2_emulation_main_t l2_emulation_main;
+
+/**
+ * A zero'd out struct we can use in the vec_validate
+ */
+static const l2_emulation_t ezero = { };
+
+__clib_export void
+l2_emulation_enable (u32 sw_if_index)
+{
+ l2_emulation_main_t *em = &l2_emulation_main;
+ vec_validate_init_empty (em->l2_emulations, sw_if_index, ezero);
+
+ l2_emulation_t *l23e = &em->l2_emulations[sw_if_index];
+
+ l23e->enabled = 1;
+
+ /*
+ * L3 enable the interface - using IP unnumbered from the control
+ * plane may not be possible since there may be no BVI interface
+ * to which to unnumber
+ */
+ ip4_sw_interface_enable_disable (sw_if_index, 1);
+ ip6_sw_interface_enable_disable (sw_if_index, 1);
+
+ l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_EMULATION, 1);
+}
+
+
+__clib_export void
+l2_emulation_disable (u32 sw_if_index)
+{
+ l2_emulation_main_t *em = &l2_emulation_main;
+ if (vec_len (em->l2_emulations) >= sw_if_index)
+ {
+ l2_emulation_t *l23e = &em->l2_emulations[sw_if_index];
+ clib_memset (l23e, 0, sizeof (*l23e));
+
+ l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_EMULATION, 0);
+ ip4_sw_interface_enable_disable (sw_if_index, 0);
+ ip6_sw_interface_enable_disable (sw_if_index, 0);
+ }
+}
+
+static clib_error_t *
+l2_emulation_interface_add_del (vnet_main_t * vnm,
+ u32 sw_if_index, u32 is_add)
+{
+ l2_emulation_main_t *em = &l2_emulation_main;
+ if (is_add)
+ {
+ vec_validate_init_empty (em->l2_emulations, sw_if_index, ezero);
+ }
+
+ return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (l2_emulation_interface_add_del);
+
+static clib_error_t *
+l2_emulation_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index = ~0;
+ u8 enable = 1;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U", unformat_vnet_sw_interface,
+ vnm, &sw_if_index))
+ ;
+ else if (unformat (input, "enable"))
+ enable = 1;
+ else if (unformat (input, "disable"))
+ enable = 0;
+ else
+ break;
+ }
+
+ if (~0 == sw_if_index)
+ return clib_error_return (0, "interface must be specified");
+
+ if (enable)
+ l2_emulation_enable (sw_if_index);
+ else
+ l2_emulation_disable (sw_if_index);
+
+ return (NULL);
+}
+
+/*?
+ * Configure l2 emulation.
+ * When the interface is in L2 mode, configure the extraction of L3
+ * packets out of the L2 path and into the L3 path.
+ *
+ * @cliexpar
+ * @cliexstart{set interface l2 input l2-emulation <interface-name> [disable]}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (l2_emulation_cli_node, static) = {
+ .path = "set interface l2 l2-emulation",
+ .short_help =
+ "set interface l2 l2-emulation <interface-name> [disable|enable]\n",
+ .function = l2_emulation_cli,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+l2_emulation_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ l2_emulation_main_t *em = &l2_emulation_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ l2_emulation_t *l23e;
+ u32 sw_if_index;
+
+ vec_foreach_index (sw_if_index, em->l2_emulations)
+ {
+ l23e = &em->l2_emulations[sw_if_index];
+ if (l23e->enabled)
+ {
+ vlib_cli_output (vm, "%U\n",
+ format_vnet_sw_if_index_name, vnm, sw_if_index);
+ }
+ }
+ return (NULL);
+}
+
+/*?
+ * Show l2 emulation.
+ * When the interface is in L2 mode, configure the extraction of L3
+ * packets out of the L2 path and into the L3 path.
+ *
+ * @cliexpar
+ * @cliexstart{show interface l2 l2-emulation}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (l2_emulation_show_node, static) = {
+ .path = "show interface l2 l2-emulation",
+ .short_help = "show interface l2 l2-emulation\n",
+ .function = l2_emulation_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+l2_emulation_init (vlib_main_t * vm)
+{
+ l2_emulation_main_t *em = &l2_emulation_main;
+ vlib_node_t *node;
+
+ node = vlib_get_node_by_name (vm, (u8 *) "l2-emulation");
+ em->l2_emulation_node_index = node->index;
+
+ /* Initialize the feature next-node indexes */
+ feat_bitmap_init_next_nodes (vm,
+ em->l2_emulation_node_index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ em->l2_input_feat_next);
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (l2_emulation_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/l2e/l2e.h b/extras/deprecated/plugins/l2e/l2e.h
new file mode 100644
index 00000000000..e548d333f9d
--- /dev/null
+++ b/extras/deprecated/plugins/l2e/l2e.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013 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_vnet_l2_emulation_h
+#define included_vnet_l2_emulation_h
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+/**
+ * Per-interface L2 configuration
+ */
+typedef struct l2_emulation_t_
+{
+ /**
+ * Enabled or Disabled.
+ * this is required since one L3 protocl can be enabled, but others not
+ */
+ u8 enabled;
+} l2_emulation_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct l2_emulation_trace_t_
+{
+ /* per-pkt trace data */
+ u8 extracted;
+} l2_emulation_trace_t;
+
+/**
+ * Grouping of global data for the L2 emulation feature
+ */
+typedef struct l2_emulation_main_t_
+{
+ u16 msg_id_base;
+
+ u32 l2_emulation_node_index;
+
+ /**
+ * Per-interface vector of emulation configs
+ */
+ l2_emulation_t *l2_emulations;
+
+ /**
+ * Next nodes for L2 output features
+ */
+ u32 l2_input_feat_next[32];
+} l2_emulation_main_t;
+
+/**
+ * L2 Emulation is a feautre that is applied to L2 ports to 'extract'
+ * IP packets from the L2 path and inject them into the L3 path (i.e.
+ * into the appropriate ip[4|6]_input node).
+ * L3 routes in the table_id for that interface should then be configured
+ * as DVR routes, therefore the forwarded packet has the L2 header
+ * preserved and togehter the L3 routed system behaves like an L2 bridge.
+ */
+extern void l2_emulation_enable (u32 sw_if_index);
+extern void l2_emulation_disable (u32 sw_if_index);
+
+extern l2_emulation_main_t l2_emulation_main;
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/l2e/l2e_api.c b/extras/deprecated/plugins/l2e/l2e_api.c
new file mode 100644
index 00000000000..fe2fb7ee06e
--- /dev/null
+++ b/extras/deprecated/plugins/l2e/l2e_api.c
@@ -0,0 +1,89 @@
+/*
+ *------------------------------------------------------------------
+ * l2e_api.c - layer 2 emulation api
+ *
+ * Copyright (c) 2016 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/interface.h>
+#include <vnet/api_errno.h>
+#include <vpp/app/version.h>
+
+#include <l2e/l2e.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+/* define message IDs */
+#include <l2e/l2e.api_enum.h>
+#include <l2e/l2e.api_types.h>
+
+#include <vlibapi/api_helper_macros.h>
+
+#define L2E_MSG_BASE l2em->msg_id_base
+
+static void
+vl_api_l2_emulation_t_handler (vl_api_l2_emulation_t * mp)
+{
+ l2_emulation_main_t *l2em = &l2_emulation_main;
+ vl_api_l2_emulation_reply_t *rmp;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+
+ if (mp->enable)
+ l2_emulation_enable (sw_if_index);
+ else
+ l2_emulation_disable (sw_if_index);
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_L2_EMULATION_REPLY + L2E_MSG_BASE);
+}
+
+#include <l2e/l2e.api.c>
+static clib_error_t *
+l2e_init (vlib_main_t * vm)
+{
+ l2_emulation_main_t *l2em = &l2_emulation_main;
+
+ /* Ask for a correctly-sized block of API message decode slots */
+ l2em->msg_id_base = setup_message_id_table ();
+
+ return (NULL);
+}
+
+VLIB_API_INIT_FUNCTION (l2e_init);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Layer 2 (L2) Emulation",
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/plugins/l2e/l2e_node.c b/extras/deprecated/plugins/l2e/l2e_node.c
new file mode 100644
index 00000000000..71c9b4bc6af
--- /dev/null
+++ b/extras/deprecated/plugins/l2e/l2e_node.c
@@ -0,0 +1,283 @@
+/*
+ * l2e_node.c : l2 emulation node
+ *
+ * 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 <plugins/l2e/l2e.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/feat_bitmap.h>
+
+#define foreach_l2_emulation \
+ _(IP4, "Extract IPv4") \
+ _(IP6, "Extract IPv6")
+
+typedef enum
+{
+#define _(sym,str) L2_EMULATION_ERROR_##sym,
+ foreach_l2_emulation
+#undef _
+ L2_EMULATION_N_ERROR,
+} l2_emulation_error_t;
+
+static char *l2_emulation_error_strings[] = {
+#define _(sym,string) string,
+ foreach_l2_emulation
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) L2_EMULATION_NEXT_##sym,
+ foreach_l2_emulation
+#undef _
+ L2_EMULATION_N_NEXT,
+} l2_emulation_next_t;
+
+/* packet trace format function */
+static u8 *
+format_l2_emulation_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 *);
+ l2_emulation_trace_t *t = va_arg (*args, l2_emulation_trace_t *);
+
+ s = format (s, "l2-emulation: %s", (t->extracted ? "yes" : "no"));
+
+ return s;
+}
+
+VLIB_NODE_FN (l2_emulation_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ l2_emulation_main_t *em = &l2_emulation_main;
+ u32 n_left_from, *from, *to_next;
+ l2_emulation_next_t next_index;
+ u32 ip4_hits = 0;
+ u32 ip6_hits = 0;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ 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 >= 4 && n_left_to_next >= 2)
+ {
+ vlib_buffer_t *b0, *b1;
+ u32 sw_if_index0, sw_if_index1;
+ u16 ether_type0, ether_type1;
+ u32 next0 = ~0, next1 = ~0;
+ u8 l2_len0, l2_len1;
+ u32 bi0, bi1;
+ u8 *h0, *h1;
+
+ bi0 = to_next[0] = from[0];
+ bi1 = to_next[1] = from[1];
+
+ from += 2;
+ n_left_from -= 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+ l2_len0 = vnet_buffer (b0)->l2.l2_len;
+ l2_len1 = vnet_buffer (b1)->l2.l2_len;
+
+ h0 = vlib_buffer_get_current (b0);
+ h1 = vlib_buffer_get_current (b1);
+
+ ether_type0 = clib_net_to_host_u16 (*(u16 *) (h0 + l2_len0 - 2));
+ ether_type1 = clib_net_to_host_u16 (*(u16 *) (h1 + l2_len1 - 2));
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+
+ /*
+ * only extract unicast
+ */
+ if (PREDICT_TRUE (!(h0[0] & 0x1)))
+ {
+ switch (ether_type0)
+ {
+ case ETHERNET_TYPE_IP4:
+ ASSERT (em->l2_emulations[sw_if_index0].enabled);
+ ++ip4_hits;
+ next0 = L2_EMULATION_NEXT_IP4;
+ vlib_buffer_advance (b0, l2_len0);
+ break;
+ case ETHERNET_TYPE_IP6:
+ ASSERT (em->l2_emulations[sw_if_index0].enabled);
+ ++ip6_hits;
+ next0 = L2_EMULATION_NEXT_IP6;
+ vlib_buffer_advance (b0, l2_len0);
+ default:
+ break;
+ }
+ }
+ if (PREDICT_TRUE (!(h1[0] & 0x1)))
+ {
+ switch (ether_type1)
+ {
+ case ETHERNET_TYPE_IP4:
+ ASSERT (em->l2_emulations[sw_if_index1].enabled);
+ ++ip4_hits;
+ next1 = L2_EMULATION_NEXT_IP4;
+ vlib_buffer_advance (b1, l2_len1);
+ break;
+ case ETHERNET_TYPE_IP6:
+ ASSERT (em->l2_emulations[sw_if_index1].enabled);
+ ++ip6_hits;
+ next1 = L2_EMULATION_NEXT_IP6;
+ vlib_buffer_advance (b1, l2_len1);
+ default:
+ break;
+ }
+ }
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ l2_emulation_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->extracted = (next0 != ~0);
+ }
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b1->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ l2_emulation_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->extracted = (next1 != ~0);
+ }
+
+ /* Determine the next node and remove ourself from bitmap */
+ if (PREDICT_TRUE (next0 == ~0))
+ next0 = vnet_l2_feature_next (b0, em->l2_input_feat_next,
+ L2INPUT_FEAT_L2_EMULATION);
+
+ /* Determine the next node and remove ourself from bitmap */
+ if (PREDICT_TRUE (next1 == ~0))
+ next1 = vnet_l2_feature_next (b1, em->l2_input_feat_next,
+ L2INPUT_FEAT_L2_EMULATION);
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t *b0;
+ u32 sw_if_index0;
+ u16 ether_type0;
+ u32 next0 = ~0;
+ u8 l2_len0;
+ u32 bi0;
+ u8 *h0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ l2_len0 = vnet_buffer (b0)->l2.l2_len;
+
+ h0 = vlib_buffer_get_current (b0);
+ ether_type0 = clib_net_to_host_u16 (*(u16 *) (h0 + l2_len0 - 2));
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+ /*
+ * only extract unicast
+ */
+ if (PREDICT_TRUE (!(h0[0] & 0x1)))
+ {
+ switch (ether_type0)
+ {
+ case ETHERNET_TYPE_IP4:
+ ASSERT (em->l2_emulations[sw_if_index0].enabled);
+ ++ip4_hits;
+ next0 = L2_EMULATION_NEXT_IP4;
+ vlib_buffer_advance (b0, l2_len0);
+ break;
+ case ETHERNET_TYPE_IP6:
+ ASSERT (em->l2_emulations[sw_if_index0].enabled);
+ ++ip6_hits;
+ next0 = L2_EMULATION_NEXT_IP6;
+ vlib_buffer_advance (b0, l2_len0);
+ default:
+ break;
+ }
+ }
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ l2_emulation_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->extracted = (next0 != ~0);
+ }
+
+ /* Determine the next node and remove ourself from bitmap */
+ if (PREDICT_TRUE (next0 == ~0))
+ next0 = vnet_l2_feature_next (b0, em->l2_input_feat_next,
+ L2INPUT_FEAT_L2_EMULATION);
+
+ /* 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,
+ L2_EMULATION_ERROR_IP4, ip4_hits);
+ vlib_node_increment_counter (vm, node->node_index,
+ L2_EMULATION_ERROR_IP6, ip6_hits);
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (l2_emulation_node) = {
+ .name = "l2-emulation",
+ .vector_size = sizeof (u32),
+ .format_trace = format_l2_emulation_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(l2_emulation_error_strings),
+ .error_strings = l2_emulation_error_strings,
+
+ .n_next_nodes = L2_EMULATION_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [L2_EMULATION_NEXT_IP4] = "ip4-input",
+ [L2_EMULATION_NEXT_IP6] = "ip6-input",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/lawful-intercept/lawful_intercept.c b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.c
new file mode 100644
index 00000000000..61b1a6165f4
--- /dev/null
+++ b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.c
@@ -0,0 +1,122 @@
+/*
+ * 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 <vnet/lawful-intercept/lawful_intercept.h>
+
+li_main_t li_main;
+
+static clib_error_t *
+set_li_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ li_main_t *lm = &li_main;
+ ip4_address_t collector;
+ u8 collector_set = 0;
+ ip4_address_t src;
+ u8 src_set = 0;
+ u32 tmp;
+ u16 udp_port = 0;
+ u8 is_add = 1;
+ int i;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "collector %U", unformat_ip4_address, &collector))
+ collector_set = 1;
+ if (unformat (input, "src %U", unformat_ip4_address, &src))
+ src_set = 1;
+ else if (unformat (input, "udp-port %d", &tmp))
+ udp_port = tmp;
+ else if (unformat (input, "del"))
+ is_add = 0;
+ else
+ break;
+ }
+
+ if (collector_set == 0)
+ return clib_error_return (0, "collector must be set...");
+ if (src_set == 0)
+ return clib_error_return (0, "src must be set...");
+ if (udp_port == 0)
+ return clib_error_return (0, "udp-port must be set...");
+
+ if (is_add == 1)
+ {
+ for (i = 0; i < vec_len (lm->collectors); i++)
+ {
+ if (lm->collectors[i].as_u32 == collector.as_u32)
+ {
+ if (lm->ports[i] == udp_port)
+ return clib_error_return (
+ 0, "collector %U:%d already configured", format_ip4_address,
+ &collector, udp_port);
+ else
+ return clib_error_return (
+ 0, "collector %U already configured with port %d",
+ format_ip4_address, &collector, (int) (lm->ports[i]));
+ }
+ }
+ vec_add1 (lm->collectors, collector);
+ vec_add1 (lm->ports, udp_port);
+ vec_add1 (lm->src_addrs, src);
+ return 0;
+ }
+ else
+ {
+ for (i = 0; i < vec_len (lm->collectors); i++)
+ {
+ if ((lm->collectors[i].as_u32 == collector.as_u32)
+ && lm->ports[i] == udp_port)
+ {
+ vec_delete (lm->collectors, 1, i);
+ vec_delete (lm->ports, 1, i);
+ vec_delete (lm->src_addrs, 1, i);
+ return 0;
+ }
+ }
+ return clib_error_return (0, "collector %U:%d not configured",
+ &collector, udp_port);
+ }
+ return 0;
+}
+
+VLIB_CLI_COMMAND (set_li_command, static) = {
+ .path = "set li",
+ .short_help =
+ "set li src <ip4-address> collector <ip4-address> udp-port <nnnn>",
+ .function = set_li_command_fn,
+};
+
+static clib_error_t *
+li_init (vlib_main_t * vm)
+{
+ li_main_t *lm = &li_main;
+
+ lm->vlib_main = vm;
+ lm->vnet_main = vnet_get_main ();
+ lm->hit_node_index = li_hit_node.index;
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (li_init);
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/lawful-intercept/lawful_intercept.h b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.h
new file mode 100644
index 00000000000..ba74204fb9e
--- /dev/null
+++ b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.h
@@ -0,0 +1,54 @@
+/*
+ * 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 __lawful_intercept_h__
+#define __lawful_intercept_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+
+typedef struct
+{
+ /* LI collector info */
+ ip4_address_t *src_addrs;
+ ip4_address_t *collectors;
+ u16 *ports;
+
+ /* Hit node index */
+ u32 hit_node_index;
+
+ /* convenience */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+} li_main_t;
+
+extern li_main_t li_main;
+
+typedef CLIB_PACKED(struct {
+ ip4_header_t ip4;
+ udp_header_t udp;
+}) ip4_udp_header_t;
+
+extern vlib_node_registration_t li_hit_node;
+
+#endif /* __lawful_intercept_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/lawful-intercept/node.c b/extras/deprecated/vnet/lawful-intercept/node.c
new file mode 100644
index 00000000000..86f135b9ea1
--- /dev/null
+++ b/extras/deprecated/vnet/lawful-intercept/node.c
@@ -0,0 +1,286 @@
+/*
+ * 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 <vppinfra/error.h>
+
+#include <vnet/lawful-intercept/lawful_intercept.h>
+
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+extern vlib_node_registration_t li_hit_node;
+
+typedef struct
+{
+ u32 next_index;
+} li_hit_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_li_hit_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 *);
+ li_hit_trace_t *t = va_arg (*args, li_hit_trace_t *);
+
+ s = format (s, "LI_HIT: next index %d", t->next_index);
+
+ return s;
+}
+
+#define foreach_li_hit_error \
+_(HITS, "LI packets processed") \
+_(NO_COLLECTOR, "No collector configured") \
+_(BUFFER_ALLOCATION_FAILURE, "Buffer allocation failure")
+
+typedef enum
+{
+#define _(sym,str) LI_HIT_ERROR_##sym,
+ foreach_li_hit_error
+#undef _
+ LI_HIT_N_ERROR,
+} li_hit_error_t;
+
+static char *li_hit_error_strings[] = {
+#define _(sym,string) string,
+ foreach_li_hit_error
+#undef _
+};
+
+typedef enum
+{
+ LI_HIT_NEXT_ETHERNET,
+ LI_HIT_N_NEXT,
+} li_hit_next_t;
+
+VLIB_NODE_FN (li_hit_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ li_hit_next_t next_index;
+ vlib_frame_t *int_frame = 0;
+ u32 *to_int_next = 0;
+ li_main_t *lm = &li_main;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (PREDICT_FALSE (vec_len (lm->collectors) == 0))
+ {
+ vlib_node_increment_counter (vm, li_hit_node.index,
+ LI_HIT_ERROR_NO_COLLECTOR, n_left_from);
+ }
+ else
+ {
+ /* The intercept frame... */
+ int_frame = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+ to_int_next = vlib_frame_vector_args (int_frame);
+ }
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+#if 0
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 next0 = LI_HIT_NEXT_INTERFACE_OUTPUT;
+ u32 next1 = LI_HIT_NEXT_INTERFACE_OUTPUT;
+ u32 sw_if_index0, sw_if_index1;
+ u8 tmp0[6], tmp1[6];
+ ethernet_header_t *en0, *en1;
+ u32 bi0, bi1;
+ vlib_buffer_t *b0, *b1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ clib_prefetch_store (p2->data);
+ clib_prefetch_store (p3->data);
+ }
+
+ /* speculatively enqueue b0 and b1 to the current next frame */
+ to_next[0] = bi0 = from[0];
+ to_next[1] = bi1 = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
+ ASSERT (b0->current_data == 0);
+ ASSERT (b1->current_data == 0);
+
+ en0 = vlib_buffer_get_current (b0);
+ en1 = vlib_buffer_get_current (b1);
+
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+
+ /* Send pkt back out the RX interface */
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
+ vnet_buffer (b1)->sw_if_index[VLIB_TX] = sw_if_index1;
+
+ /* $$$$$ End of processing 2 x packets $$$$$ */
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+ {
+ if (b0->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ li_hit_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+ if (b1->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ li_hit_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->sw_if_index = sw_if_index1;
+ t->next_index = next1;
+ }
+ }
+
+ /* verify speculative enqueues, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+#endif /* $$$ dual-loop off */
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ vlib_buffer_t *c0;
+ ip4_udp_header_t *iu0;
+ ip4_header_t *ip0;
+ udp_header_t *udp0;
+ u32 next0 = LI_HIT_NEXT_ETHERNET;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ if (PREDICT_TRUE (to_int_next != 0))
+ {
+ /* Make an intercept copy. This can fail. */
+ c0 = vlib_buffer_copy (vm, b0);
+
+ if (PREDICT_FALSE (c0 == 0))
+ {
+ vlib_node_increment_counter
+ (vm, node->node_index,
+ LI_HIT_ERROR_BUFFER_ALLOCATION_FAILURE, 1);
+ goto skip;
+ }
+
+ vlib_buffer_advance (c0, -sizeof (*iu0));
+
+ iu0 = vlib_buffer_get_current (c0);
+ ip0 = &iu0->ip4;
+
+ ip0->ip_version_and_header_length = 0x45;
+ ip0->ttl = 254;
+ ip0->protocol = IP_PROTOCOL_UDP;
+
+ ip0->src_address.as_u32 = lm->src_addrs[0].as_u32;
+ ip0->dst_address.as_u32 = lm->collectors[0].as_u32;
+ ip0->length = vlib_buffer_length_in_chain (vm, c0);
+ ip0->checksum = ip4_header_checksum (ip0);
+
+ udp0 = &iu0->udp;
+ udp0->src_port = udp0->dst_port =
+ clib_host_to_net_u16 (lm->ports[0]);
+ udp0->checksum = 0;
+ udp0->length =
+ clib_net_to_host_u16 (vlib_buffer_length_in_chain (vm, b0));
+
+ to_int_next[0] = vlib_get_buffer_index (vm, c0);
+ to_int_next++;
+ }
+
+ skip:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ li_hit_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ }
+
+ /* 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);
+ }
+
+ if (int_frame)
+ {
+ int_frame->n_vectors = frame->n_vectors;
+ vlib_put_frame_to_node (vm, ip4_lookup_node.index, int_frame);
+ }
+
+ vlib_node_increment_counter (vm, li_hit_node.index,
+ LI_HIT_ERROR_HITS, frame->n_vectors);
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (li_hit_node) = {
+ .name = "li-hit",
+ .vector_size = sizeof (u32),
+ .format_trace = format_li_hit_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(li_hit_error_strings),
+ .error_strings = li_hit_error_strings,
+
+ .n_next_nodes = LI_HIT_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [LI_HIT_NEXT_ETHERNET] = "ethernet-input-not-l2",
+ },
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/vxlan-gbp/decap.c b/extras/deprecated/vnet/vxlan-gbp/decap.c
new file mode 100644
index 00000000000..927c778b211
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/decap.c
@@ -0,0 +1,1050 @@
+/*
+ * decap.c: vxlan gbp tunnel decap packet processing
+ *
+ * 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 <vlib/vlib.h>
+
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+
+typedef struct
+{
+ u32 next_index;
+ u32 tunnel_index;
+ u32 error;
+ u32 vni;
+ u16 sclass;
+ u8 flags;
+} vxlan_gbp_rx_trace_t;
+
+static u8 *
+format_vxlan_gbp_rx_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 *);
+ vxlan_gbp_rx_trace_t *t = va_arg (*args, vxlan_gbp_rx_trace_t *);
+
+ if (t->tunnel_index == ~0)
+ return format (s,
+ "VXLAN_GBP decap error - tunnel for vni %d does not exist",
+ t->vni);
+ return format (s,
+ "VXLAN_GBP decap from vxlan_gbp_tunnel%d vni %d sclass %d"
+ " flags %U next %d error %d",
+ t->tunnel_index, t->vni, t->sclass,
+ format_vxlan_gbp_header_gpflags, t->flags,
+ t->next_index, t->error);
+}
+
+always_inline u32
+buf_fib_index (vlib_buffer_t * b, u32 is_ip4)
+{
+ u32 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
+ if (sw_if_index != (u32) ~ 0)
+ return sw_if_index;
+
+ u32 *fib_index_by_sw_if_index = is_ip4 ?
+ ip4_main.fib_index_by_sw_if_index : ip6_main.fib_index_by_sw_if_index;
+ sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
+
+ return vec_elt (fib_index_by_sw_if_index, sw_if_index);
+}
+
+typedef vxlan4_gbp_tunnel_key_t last_tunnel_cache4;
+
+always_inline vxlan_gbp_tunnel_t *
+vxlan4_gbp_find_tunnel (vxlan_gbp_main_t * vxm, last_tunnel_cache4 * cache,
+ u32 fib_index, ip4_header_t * ip4_0,
+ vxlan_gbp_header_t * vxlan_gbp0)
+{
+ /*
+ * Check unicast first since that's where most of the traffic comes from
+ * Make sure VXLAN_GBP tunnel exist according to packet SIP, DIP and VNI
+ */
+ vxlan4_gbp_tunnel_key_t key4;
+ int rv;
+
+ key4.key[1] = (((u64) fib_index << 32) |
+ (vxlan_gbp0->vni_reserved &
+ clib_host_to_net_u32 (0xffffff00)));
+ key4.key[0] =
+ (((u64) ip4_0->dst_address.as_u32 << 32) | ip4_0->src_address.as_u32);
+
+ if (PREDICT_FALSE (key4.key[0] != cache->key[0] ||
+ key4.key[1] != cache->key[1]))
+ {
+ rv = clib_bihash_search_inline_16_8 (&vxm->vxlan4_gbp_tunnel_by_key,
+ &key4);
+ if (PREDICT_FALSE (rv == 0))
+ {
+ *cache = key4;
+ return (pool_elt_at_index (vxm->tunnels, cache->value));
+ }
+ }
+ else
+ {
+ return (pool_elt_at_index (vxm->tunnels, cache->value));
+ }
+
+ /* No unicast match - try multicast */
+ if (PREDICT_TRUE (!ip4_address_is_multicast (&ip4_0->dst_address)))
+ return (NULL);
+
+ key4.key[0] = ip4_0->dst_address.as_u32;
+ /* Make sure mcast VXLAN_GBP tunnel exist by packet DIP and VNI */
+ rv = clib_bihash_search_inline_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, &key4);
+
+ if (PREDICT_FALSE (rv != 0))
+ return (NULL);
+
+ return (pool_elt_at_index (vxm->tunnels, key4.value));
+}
+
+typedef vxlan6_gbp_tunnel_key_t last_tunnel_cache6;
+
+always_inline vxlan_gbp_tunnel_t *
+vxlan6_gbp_find_tunnel (vxlan_gbp_main_t * vxm, last_tunnel_cache6 * cache,
+ u32 fib_index, ip6_header_t * ip6_0,
+ vxlan_gbp_header_t * vxlan_gbp0)
+{
+ /* Make sure VXLAN_GBP tunnel exist according to packet SIP and VNI */
+ vxlan6_gbp_tunnel_key_t key6 = {
+ .key = {
+ [0] = ip6_0->src_address.as_u64[0],
+ [1] = ip6_0->src_address.as_u64[1],
+ [2] = ((((u64) fib_index) << 32) |
+ (vxlan_gbp0->vni_reserved &
+ clib_host_to_net_u32 (0xffffff00))),
+ }
+ };
+ int rv;
+
+ if (PREDICT_FALSE
+ (clib_bihash_key_compare_24_8 (key6.key, cache->key) == 0))
+ {
+ rv = clib_bihash_search_inline_24_8 (&vxm->vxlan6_gbp_tunnel_by_key,
+ &key6);
+ if (PREDICT_FALSE (rv != 0))
+ return NULL;
+
+ *cache = key6;
+ }
+ vxlan_gbp_tunnel_t *t0 = pool_elt_at_index (vxm->tunnels, cache->value);
+
+ /* Validate VXLAN_GBP tunnel SIP against packet DIP */
+ if (PREDICT_FALSE
+ (!ip6_address_is_equal (&ip6_0->dst_address, &t0->src.ip6)))
+ {
+ /* try multicast */
+ if (PREDICT_TRUE (!ip6_address_is_multicast (&ip6_0->dst_address)))
+ return 0;
+
+ /* Make sure mcast VXLAN_GBP tunnel exist by packet DIP and VNI */
+ key6.key[0] = ip6_0->dst_address.as_u64[0];
+ key6.key[1] = ip6_0->dst_address.as_u64[1];
+ rv = clib_bihash_search_inline_24_8 (&vxm->vxlan6_gbp_tunnel_by_key,
+ &key6);
+ if (PREDICT_FALSE (rv != 0))
+ return 0;
+
+ }
+
+ return t0;
+}
+
+always_inline vxlan_gbp_input_next_t
+vxlan_gbp_tunnel_get_next (const vxlan_gbp_tunnel_t * t, vlib_buffer_t * b0)
+{
+ if (VXLAN_GBP_TUNNEL_MODE_L2 == t->mode)
+ return (VXLAN_GBP_INPUT_NEXT_L2_INPUT);
+ else
+ {
+ ethernet_header_t *e0;
+ u16 type0;
+
+ e0 = vlib_buffer_get_current (b0);
+ vlib_buffer_advance (b0, sizeof (*e0));
+ type0 = clib_net_to_host_u16 (e0->type);
+ switch (type0)
+ {
+ case ETHERNET_TYPE_IP4:
+ return (VXLAN_GBP_INPUT_NEXT_IP4_INPUT);
+ case ETHERNET_TYPE_IP6:
+ return (VXLAN_GBP_INPUT_NEXT_IP6_INPUT);
+ }
+ }
+ return (VXLAN_GBP_INPUT_NEXT_DROP);
+}
+
+always_inline uword
+vxlan_gbp_input (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame, u8 is_ip4)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vnet_main_t *vnm = vxm->vnet_main;
+ vnet_interface_main_t *im = &vnm->interface_main;
+ vlib_combined_counter_main_t *rx_counter =
+ im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX;
+ vlib_combined_counter_main_t *drop_counter =
+ im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP;
+ last_tunnel_cache4 last4;
+ last_tunnel_cache6 last6;
+ u32 pkts_decapsulated = 0;
+ u32 thread_index = vlib_get_thread_index ();
+
+ if (is_ip4)
+ clib_memset (&last4, 0xff, sizeof last4);
+ else
+ clib_memset (&last6, 0xff, sizeof last6);
+
+ u32 next_index = node->cached_next_index;
+
+ u32 *from = vlib_frame_vector_args (from_frame);
+ u32 n_left_from = from_frame->n_vectors;
+
+ while (n_left_from > 0)
+ {
+ u32 *to_next, n_left_to_next;
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ u32 bi0 = to_next[0] = from[0];
+ u32 bi1 = to_next[1] = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+ n_left_from -= 2;
+
+ vlib_buffer_t *b0, *b1;
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ /* udp leaves current_data pointing at the vxlan_gbp header */
+ void *cur0 = vlib_buffer_get_current (b0);
+ void *cur1 = vlib_buffer_get_current (b1);
+ vxlan_gbp_header_t *vxlan_gbp0 = cur0;
+ vxlan_gbp_header_t *vxlan_gbp1 = cur1;
+
+ ip4_header_t *ip4_0, *ip4_1;
+ ip6_header_t *ip6_0, *ip6_1;
+ if (is_ip4)
+ {
+ ip4_0 = cur0 - sizeof (udp_header_t) - sizeof (ip4_header_t);
+ ip4_1 = cur1 - sizeof (udp_header_t) - sizeof (ip4_header_t);
+ }
+ else
+ {
+ ip6_0 = cur0 - sizeof (udp_header_t) - sizeof (ip6_header_t);
+ ip6_1 = cur1 - sizeof (udp_header_t) - sizeof (ip6_header_t);
+ }
+
+ u32 fi0 = buf_fib_index (b0, is_ip4);
+ u32 fi1 = buf_fib_index (b1, is_ip4);
+
+ vxlan_gbp_tunnel_t *t0, *t1;
+ if (is_ip4)
+ {
+ t0 =
+ vxlan4_gbp_find_tunnel (vxm, &last4, fi0, ip4_0, vxlan_gbp0);
+ t1 =
+ vxlan4_gbp_find_tunnel (vxm, &last4, fi1, ip4_1, vxlan_gbp1);
+ }
+ else
+ {
+ t0 =
+ vxlan6_gbp_find_tunnel (vxm, &last6, fi0, ip6_0, vxlan_gbp0);
+ t1 =
+ vxlan6_gbp_find_tunnel (vxm, &last6, fi1, ip6_1, vxlan_gbp1);
+ }
+
+ u32 len0 = vlib_buffer_length_in_chain (vm, b0);
+ u32 len1 = vlib_buffer_length_in_chain (vm, b1);
+
+ vxlan_gbp_input_next_t next0, next1;
+ u8 error0 = 0, error1 = 0;
+ u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0);
+ u8 flags1 = vxlan_gbp_get_flags (vxlan_gbp1);
+ /* Required to make the l2 tag push / pop code work on l2 subifs */
+ /* pop vxlan_gbp */
+ vlib_buffer_advance (b0, sizeof *vxlan_gbp0);
+ vlib_buffer_advance (b1, sizeof *vxlan_gbp1);
+
+ u8 i_and_g0 = ((flags0 & VXLAN_GBP_FLAGS_GI) == VXLAN_GBP_FLAGS_GI);
+ u8 i_and_g1 = ((flags1 & VXLAN_GBP_FLAGS_GI) == VXLAN_GBP_FLAGS_GI);
+
+ /* Validate VXLAN_GBP tunnel encap-fib index against packet */
+ if (PREDICT_FALSE (t0 == NULL || !i_and_g0))
+ {
+ if (t0 != NULL && !i_and_g0)
+ {
+ error0 = VXLAN_GBP_ERROR_BAD_FLAGS;
+ vlib_increment_combined_counter
+ (drop_counter, thread_index, t0->sw_if_index, 1, len0);
+ next0 = VXLAN_GBP_INPUT_NEXT_DROP;
+ }
+ else
+ {
+ error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+ next0 = VXLAN_GBP_INPUT_NEXT_PUNT;
+ if (is_ip4)
+ b0->punt_reason =
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4];
+ else
+ b0->punt_reason =
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6];
+ }
+ b0->error = node->errors[error0];
+ }
+ else
+ {
+ next0 = vxlan_gbp_tunnel_get_next (t0, b0);
+
+ /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;
+ vlib_increment_combined_counter
+ (rx_counter, thread_index, t0->sw_if_index, 1, len0);
+ pkts_decapsulated++;
+ }
+
+ vnet_buffer2 (b0)->gbp.flags = (vxlan_gbp_get_gpflags (vxlan_gbp0) |
+ VXLAN_GBP_GPFLAGS_R);
+ vnet_buffer2 (b0)->gbp.sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+
+
+ if (PREDICT_FALSE (t1 == NULL || !i_and_g1))
+ {
+ if (t1 != NULL && !i_and_g1)
+ {
+ error1 = VXLAN_GBP_ERROR_BAD_FLAGS;
+ vlib_increment_combined_counter
+ (drop_counter, thread_index, t1->sw_if_index, 1, len1);
+ next1 = VXLAN_GBP_INPUT_NEXT_DROP;
+ }
+ else
+ {
+ error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+ next1 = VXLAN_GBP_INPUT_NEXT_PUNT;
+ if (is_ip4)
+ b1->punt_reason =
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4];
+ else
+ b1->punt_reason =
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6];
+ }
+ b1->error = node->errors[error1];
+ }
+ else
+ {
+ next1 = vxlan_gbp_tunnel_get_next (t1, b1);
+
+ /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */
+ vnet_buffer (b1)->sw_if_index[VLIB_RX] = t1->sw_if_index;
+ pkts_decapsulated++;
+
+ vlib_increment_combined_counter
+ (rx_counter, thread_index, t1->sw_if_index, 1, len1);
+ }
+
+ vnet_buffer2 (b1)->gbp.flags = (vxlan_gbp_get_gpflags (vxlan_gbp1) |
+ VXLAN_GBP_GPFLAGS_R);
+
+ vnet_buffer2 (b1)->gbp.sclass = vxlan_gbp_get_sclass (vxlan_gbp1);
+
+ vnet_update_l2_len (b0);
+ vnet_update_l2_len (b1);
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ vxlan_gbp_rx_trace_t *tr =
+ vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->next_index = next0;
+ tr->error = error0;
+ tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels;
+ tr->vni = vxlan_gbp_get_vni (vxlan_gbp0);
+ tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+ tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+ }
+ if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ vxlan_gbp_rx_trace_t *tr =
+ vlib_add_trace (vm, node, b1, sizeof (*tr));
+ tr->next_index = next1;
+ tr->error = error1;
+ tr->tunnel_index = t1 == 0 ? ~0 : t1 - vxm->tunnels;
+ tr->vni = vxlan_gbp_get_vni (vxlan_gbp1);
+ tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp1);
+ tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp1);
+ }
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0 = to_next[0] = from[0];
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
+
+ /* udp leaves current_data pointing at the vxlan_gbp header */
+ void *cur0 = vlib_buffer_get_current (b0);
+ vxlan_gbp_header_t *vxlan_gbp0 = cur0;
+ ip4_header_t *ip4_0;
+ ip6_header_t *ip6_0;
+ if (is_ip4)
+ ip4_0 = cur0 - sizeof (udp_header_t) - sizeof (ip4_header_t);
+ else
+ ip6_0 = cur0 - sizeof (udp_header_t) - sizeof (ip6_header_t);
+
+ u32 fi0 = buf_fib_index (b0, is_ip4);
+
+ vxlan_gbp_tunnel_t *t0;
+ if (is_ip4)
+ t0 = vxlan4_gbp_find_tunnel (vxm, &last4, fi0, ip4_0, vxlan_gbp0);
+ else
+ t0 = vxlan6_gbp_find_tunnel (vxm, &last6, fi0, ip6_0, vxlan_gbp0);
+
+ uword len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ vxlan_gbp_input_next_t next0;
+ u8 error0 = 0;
+ u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0);
+
+ /* pop (ip, udp, vxlan_gbp) */
+ vlib_buffer_advance (b0, sizeof (*vxlan_gbp0));
+
+ u8 i_and_g0 = ((flags0 & VXLAN_GBP_FLAGS_GI) == VXLAN_GBP_FLAGS_GI);
+
+ /* Validate VXLAN_GBP tunnel encap-fib index against packet */
+ if (PREDICT_FALSE (t0 == NULL || !i_and_g0))
+ {
+ if (t0 != NULL && !i_and_g0)
+ {
+ error0 = VXLAN_GBP_ERROR_BAD_FLAGS;
+ vlib_increment_combined_counter
+ (drop_counter, thread_index, t0->sw_if_index, 1, len0);
+ next0 = VXLAN_GBP_INPUT_NEXT_DROP;
+ }
+ else
+ {
+ error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+ next0 = VXLAN_GBP_INPUT_NEXT_PUNT;
+ if (is_ip4)
+ b0->punt_reason =
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4];
+ else
+ b0->punt_reason =
+ vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6];
+ }
+ b0->error = node->errors[error0];
+ }
+ else
+ {
+ next0 = vxlan_gbp_tunnel_get_next (t0, b0);
+ /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;
+ pkts_decapsulated++;
+
+ vlib_increment_combined_counter
+ (rx_counter, thread_index, t0->sw_if_index, 1, len0);
+ }
+ vnet_buffer2 (b0)->gbp.flags = (vxlan_gbp_get_gpflags (vxlan_gbp0) |
+ VXLAN_GBP_GPFLAGS_R);
+
+ vnet_buffer2 (b0)->gbp.sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+
+ /* Required to make the l2 tag push / pop code work on l2 subifs */
+ vnet_update_l2_len (b0);
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ vxlan_gbp_rx_trace_t *tr
+ = vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->next_index = next0;
+ tr->error = error0;
+ tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels;
+ tr->vni = vxlan_gbp_get_vni (vxlan_gbp0);
+ tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+ tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+ }
+ 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);
+ }
+ /* Do we still need this now that tunnel tx stats is kept? */
+ u32 node_idx =
+ is_ip4 ? vxlan4_gbp_input_node.index : vxlan6_gbp_input_node.index;
+ vlib_node_increment_counter (vm, node_idx, VXLAN_GBP_ERROR_DECAPSULATED,
+ pkts_decapsulated);
+
+ return from_frame->n_vectors;
+}
+
+VLIB_NODE_FN (vxlan4_gbp_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return vxlan_gbp_input (vm, node, from_frame, /* is_ip4 */ 1);
+}
+
+VLIB_NODE_FN (vxlan6_gbp_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ return vxlan_gbp_input (vm, node, from_frame, /* is_ip4 */ 0);
+}
+
+static char *vxlan_gbp_error_strings[] = {
+#define vxlan_gbp_error(n,s) s,
+#include <vnet/vxlan-gbp/vxlan_gbp_error.def>
+#undef vxlan_gbp_error
+#undef _
+};
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (vxlan4_gbp_input_node) =
+{
+ .name = "vxlan4-gbp-input",
+ .vector_size = sizeof (u32),
+ .n_errors = VXLAN_GBP_N_ERROR,
+ .error_strings = vxlan_gbp_error_strings,
+ .n_next_nodes = VXLAN_GBP_INPUT_N_NEXT,
+ .format_trace = format_vxlan_gbp_rx_trace,
+ .next_nodes = {
+#define _(s,n) [VXLAN_GBP_INPUT_NEXT_##s] = n,
+ foreach_vxlan_gbp_input_next
+#undef _
+ },
+};
+
+VLIB_REGISTER_NODE (vxlan6_gbp_input_node) =
+{
+ .name = "vxlan6-gbp-input",
+ .vector_size = sizeof (u32),
+ .n_errors = VXLAN_GBP_N_ERROR,
+ .error_strings = vxlan_gbp_error_strings,
+ .n_next_nodes = VXLAN_GBP_INPUT_N_NEXT,
+ .next_nodes = {
+#define _(s,n) [VXLAN_GBP_INPUT_NEXT_##s] = n,
+ foreach_vxlan_gbp_input_next
+#undef _
+ },
+ .format_trace = format_vxlan_gbp_rx_trace,
+};
+/* *INDENT-ON* */
+
+typedef enum
+{
+ IP_VXLAN_GBP_BYPASS_NEXT_DROP,
+ IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP,
+ IP_VXLAN_GBP_BYPASS_N_NEXT,
+} ip_vxlan_gbp_bypass_next_t;
+
+always_inline uword
+ip_vxlan_gbp_bypass_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, u32 is_ip4)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip4_input_node.index);
+ ip4_address_t addr4; /* last IPv4 address matching a local VTEP address */
+ ip6_address_t addr6; /* last IPv6 address matching a local VTEP address */
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ ip4_forward_next_trace (vm, node, frame, VLIB_TX);
+
+ if (is_ip4)
+ addr4.data_u32 = ~0;
+ else
+ ip6_address_set_zero (&addr6);
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ vlib_buffer_t *b0, *b1;
+ ip4_header_t *ip40, *ip41;
+ ip6_header_t *ip60, *ip61;
+ udp_header_t *udp0, *udp1;
+ u32 bi0, ip_len0, udp_len0, flags0, next0;
+ u32 bi1, ip_len1, udp_len1, flags1, next1;
+ i32 len_diff0, len_diff1;
+ u8 error0, good_udp0, proto0;
+ u8 error1, good_udp1, proto1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ bi0 = to_next[0] = from[0];
+ bi1 = to_next[1] = from[1];
+ from += 2;
+ n_left_from -= 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+ if (is_ip4)
+ {
+ ip40 = vlib_buffer_get_current (b0);
+ ip41 = vlib_buffer_get_current (b1);
+ }
+ else
+ {
+ ip60 = vlib_buffer_get_current (b0);
+ ip61 = vlib_buffer_get_current (b1);
+ }
+
+ /* Setup packet for next IP feature */
+ vnet_feature_next (&next0, b0);
+ vnet_feature_next (&next1, b1);
+
+ if (is_ip4)
+ {
+ /* Treat IP frag packets as "experimental" protocol for now
+ until support of IP frag reassembly is implemented */
+ proto0 = ip4_is_fragment (ip40) ? 0xfe : ip40->protocol;
+ proto1 = ip4_is_fragment (ip41) ? 0xfe : ip41->protocol;
+ }
+ else
+ {
+ proto0 = ip60->protocol;
+ proto1 = ip61->protocol;
+ }
+
+ /* Process packet 0 */
+ if (proto0 != IP_PROTOCOL_UDP)
+ goto exit0; /* not UDP packet */
+
+ if (is_ip4)
+ udp0 = ip4_next_header (ip40);
+ else
+ udp0 = ip6_next_header (ip60);
+
+ if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp))
+ goto exit0; /* not VXLAN_GBP packet */
+
+ /* Validate DIP against VTEPs */
+ if (is_ip4)
+ {
+ if (addr4.as_u32 != ip40->dst_address.as_u32)
+ {
+ if (!hash_get (vxm->vtep4, ip40->dst_address.as_u32))
+ goto exit0; /* no local VTEP for VXLAN_GBP packet */
+ addr4 = ip40->dst_address;
+ }
+ }
+ else
+ {
+ if (!ip6_address_is_equal (&addr6, &ip60->dst_address))
+ {
+ if (!hash_get_mem (vxm->vtep6, &ip60->dst_address))
+ goto exit0; /* no local VTEP for VXLAN_GBP packet */
+ addr6 = ip60->dst_address;
+ }
+ }
+
+ 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_ip4)
+ ip_len0 = clib_net_to_host_u16 (ip40->length);
+ else
+ ip_len0 = clib_net_to_host_u16 (ip60->payload_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 ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
+ {
+ if (is_ip4)
+ flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
+ else
+ flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
+ good_udp0 =
+ (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
+ }
+ }
+
+ if (is_ip4)
+ {
+ error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
+ error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
+ }
+ else
+ {
+ error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
+ error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
+ }
+
+ next0 = error0 ?
+ IP_VXLAN_GBP_BYPASS_NEXT_DROP :
+ IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP;
+ b0->error = error0 ? error_node->errors[error0] : 0;
+
+ /* vxlan-gbp-input node expect current at VXLAN_GBP header */
+ if (is_ip4)
+ vlib_buffer_advance (b0,
+ sizeof (ip4_header_t) +
+ sizeof (udp_header_t));
+ else
+ vlib_buffer_advance (b0,
+ sizeof (ip6_header_t) +
+ sizeof (udp_header_t));
+
+ exit0:
+ /* Process packet 1 */
+ if (proto1 != IP_PROTOCOL_UDP)
+ goto exit1; /* not UDP packet */
+
+ if (is_ip4)
+ udp1 = ip4_next_header (ip41);
+ else
+ udp1 = ip6_next_header (ip61);
+
+ if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp))
+ goto exit1; /* not VXLAN_GBP packet */
+
+ /* Validate DIP against VTEPs */
+ if (is_ip4)
+ {
+ if (addr4.as_u32 != ip41->dst_address.as_u32)
+ {
+ if (!hash_get (vxm->vtep4, ip41->dst_address.as_u32))
+ goto exit1; /* no local VTEP for VXLAN_GBP packet */
+ addr4 = ip41->dst_address;
+ }
+ }
+ else
+ {
+ if (!ip6_address_is_equal (&addr6, &ip61->dst_address))
+ {
+ if (!hash_get_mem (vxm->vtep6, &ip61->dst_address))
+ goto exit1; /* no local VTEP for VXLAN_GBP packet */
+ addr6 = ip61->dst_address;
+ }
+ }
+
+ flags1 = b1->flags;
+ good_udp1 = (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
+
+ /* Don't verify UDP checksum for packets with explicit zero checksum. */
+ good_udp1 |= udp1->checksum == 0;
+
+ /* Verify UDP length */
+ if (is_ip4)
+ ip_len1 = clib_net_to_host_u16 (ip41->length);
+ else
+ ip_len1 = clib_net_to_host_u16 (ip61->payload_length);
+ udp_len1 = clib_net_to_host_u16 (udp1->length);
+ len_diff1 = ip_len1 - udp_len1;
+
+ /* Verify UDP checksum */
+ if (PREDICT_FALSE (!good_udp1))
+ {
+ if ((flags1 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
+ {
+ if (is_ip4)
+ flags1 = ip4_tcp_udp_validate_checksum (vm, b1);
+ else
+ flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, b1);
+ good_udp1 =
+ (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
+ }
+ }
+
+ if (is_ip4)
+ {
+ error1 = good_udp1 ? 0 : IP4_ERROR_UDP_CHECKSUM;
+ error1 = (len_diff1 >= 0) ? error1 : IP4_ERROR_UDP_LENGTH;
+ }
+ else
+ {
+ error1 = good_udp1 ? 0 : IP6_ERROR_UDP_CHECKSUM;
+ error1 = (len_diff1 >= 0) ? error1 : IP6_ERROR_UDP_LENGTH;
+ }
+
+ next1 = error1 ?
+ IP_VXLAN_GBP_BYPASS_NEXT_DROP :
+ IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP;
+ b1->error = error1 ? error_node->errors[error1] : 0;
+
+ /* vxlan_gbp-input node expect current at VXLAN_GBP header */
+ if (is_ip4)
+ vlib_buffer_advance (b1,
+ sizeof (ip4_header_t) +
+ sizeof (udp_header_t));
+ else
+ vlib_buffer_advance (b1,
+ sizeof (ip6_header_t) +
+ sizeof (udp_header_t));
+
+ exit1:
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t *b0;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ udp_header_t *udp0;
+ u32 bi0, ip_len0, udp_len0, flags0, next0;
+ i32 len_diff0;
+ u8 error0, good_udp0, proto0;
+
+ 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);
+ if (is_ip4)
+ ip40 = vlib_buffer_get_current (b0);
+ else
+ ip60 = vlib_buffer_get_current (b0);
+
+ /* Setup packet for next IP feature */
+ vnet_feature_next (&next0, b0);
+
+ if (is_ip4)
+ /* Treat IP4 frag packets as "experimental" protocol for now
+ until support of IP frag reassembly is implemented */
+ proto0 = ip4_is_fragment (ip40) ? 0xfe : ip40->protocol;
+ else
+ proto0 = ip60->protocol;
+
+ if (proto0 != IP_PROTOCOL_UDP)
+ goto exit; /* not UDP packet */
+
+ if (is_ip4)
+ udp0 = ip4_next_header (ip40);
+ else
+ udp0 = ip6_next_header (ip60);
+
+ if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp))
+ goto exit; /* not VXLAN_GBP packet */
+
+ /* Validate DIP against VTEPs */
+ if (is_ip4)
+ {
+ if (addr4.as_u32 != ip40->dst_address.as_u32)
+ {
+ if (!hash_get (vxm->vtep4, ip40->dst_address.as_u32))
+ goto exit; /* no local VTEP for VXLAN_GBP packet */
+ addr4 = ip40->dst_address;
+ }
+ }
+ else
+ {
+ if (!ip6_address_is_equal (&addr6, &ip60->dst_address))
+ {
+ if (!hash_get_mem (vxm->vtep6, &ip60->dst_address))
+ goto exit; /* no local VTEP for VXLAN_GBP packet */
+ addr6 = ip60->dst_address;
+ }
+ }
+
+ 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_ip4)
+ ip_len0 = clib_net_to_host_u16 (ip40->length);
+ else
+ ip_len0 = clib_net_to_host_u16 (ip60->payload_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 ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
+ {
+ if (is_ip4)
+ flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
+ else
+ flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
+ good_udp0 =
+ (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
+ }
+ }
+
+ if (is_ip4)
+ {
+ error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
+ error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
+ }
+ else
+ {
+ error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
+ error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
+ }
+
+ next0 = error0 ?
+ IP_VXLAN_GBP_BYPASS_NEXT_DROP :
+ IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP;
+ b0->error = error0 ? error_node->errors[error0] : 0;
+
+ /* vxlan_gbp-input node expect current at VXLAN_GBP header */
+ if (is_ip4)
+ vlib_buffer_advance (b0,
+ sizeof (ip4_header_t) +
+ sizeof (udp_header_t));
+ else
+ vlib_buffer_advance (b0,
+ sizeof (ip6_header_t) +
+ sizeof (udp_header_t));
+
+ exit:
+ 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);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (ip4_vxlan_gbp_bypass_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return ip_vxlan_gbp_bypass_inline (vm, node, frame, /* is_ip4 */ 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_vxlan_gbp_bypass_node) =
+{
+ .name = "ip4-vxlan-gbp-bypass",
+ .vector_size = sizeof (u32),
+ .n_next_nodes = IP_VXLAN_GBP_BYPASS_N_NEXT,
+ .next_nodes = {
+ [IP_VXLAN_GBP_BYPASS_NEXT_DROP] = "error-drop",
+ [IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP] = "vxlan4-gbp-input",
+ },
+ .format_buffer = format_ip4_header,
+ .format_trace = format_ip4_forward_next_trace,
+};
+/* *INDENT-ON* */
+
+#ifndef CLIB_MARCH_VARIANT
+/* Dummy init function to get us linked in. */
+clib_error_t *
+ip4_vxlan_gbp_bypass_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ip4_vxlan_gbp_bypass_init);
+#endif /* CLIB_MARCH_VARIANT */
+
+VLIB_NODE_FN (ip6_vxlan_gbp_bypass_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return ip_vxlan_gbp_bypass_inline (vm, node, frame, /* is_ip4 */ 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip6_vxlan_gbp_bypass_node) =
+{
+ .name = "ip6-vxlan-gbp-bypass",
+ .vector_size = sizeof (u32),
+ .n_next_nodes = IP_VXLAN_GBP_BYPASS_N_NEXT,
+ .next_nodes = {
+ [IP_VXLAN_GBP_BYPASS_NEXT_DROP] = "error-drop",
+ [IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP] = "vxlan6-gbp-input",
+ },
+ .format_buffer = format_ip6_header,
+ .format_trace = format_ip6_forward_next_trace,
+};
+/* *INDENT-ON* */
+
+#ifndef CLIB_MARCH_VARIANT
+/* Dummy init function to get us linked in. */
+clib_error_t *
+ip6_vxlan_gbp_bypass_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ip6_vxlan_gbp_bypass_init);
+#endif /* CLIB_MARCH_VARIANT */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/netmap/dir.dox b/extras/deprecated/vnet/vxlan-gbp/dir.dox
index 7ddbf947c29..6e63c90b17b 100644
--- a/extras/deprecated/netmap/dir.dox
+++ b/extras/deprecated/vnet/vxlan-gbp/dir.dox
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Cisco and/or its affiliates.
+ * 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.
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-/* Doxygen directory documentation */
-
/**
@dir
-@brief netmap Interface Implementation.
+@brief VXLAN-GBP Code.
-This directory contains the source code for the netmap driver.
+This directory contains source code to support VXLAN-GBP.
*/
-/*? %%clicmd:group_label netmap %% ?*/
-/*? %%syscfg:group_label netmap %% ?*/
+/*? %%clicmd:group_label VXLAN-GBP CLI %% ?*/
diff --git a/extras/deprecated/vnet/vxlan-gbp/encap.c b/extras/deprecated/vnet/vxlan-gbp/encap.c
new file mode 100644
index 00000000000..2a4e8a8e312
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/encap.c
@@ -0,0 +1,601 @@
+/*
+ * 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 <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/interface_output.h>
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+#include <vnet/qos/qos_types.h>
+#include <vnet/adj/rewrite.h>
+
+/* Statistics (not all errors) */
+#define foreach_vxlan_gbp_encap_error \
+_(ENCAPSULATED, "good packets encapsulated")
+
+static char *vxlan_gbp_encap_error_strings[] = {
+#define _(sym,string) string,
+ foreach_vxlan_gbp_encap_error
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) VXLAN_GBP_ENCAP_ERROR_##sym,
+ foreach_vxlan_gbp_encap_error
+#undef _
+ VXLAN_GBP_ENCAP_N_ERROR,
+} vxlan_gbp_encap_error_t;
+
+typedef enum
+{
+ VXLAN_GBP_ENCAP_NEXT_DROP,
+ VXLAN_GBP_ENCAP_N_NEXT,
+} vxlan_gbp_encap_next_t;
+
+typedef struct
+{
+ u32 tunnel_index;
+ u32 vni;
+ u16 sclass;
+ u8 flags;
+} vxlan_gbp_encap_trace_t;
+
+#ifndef CLIB_MARCH_VARIANT
+u8 *
+format_vxlan_gbp_encap_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 *);
+ vxlan_gbp_encap_trace_t *t = va_arg (*args, vxlan_gbp_encap_trace_t *);
+
+ s =
+ format (s,
+ "VXLAN_GBP encap to vxlan_gbp_tunnel%d vni %d sclass %d flags %U",
+ t->tunnel_index, t->vni, t->sclass,
+ format_vxlan_gbp_header_gpflags, t->flags);
+ return s;
+}
+#endif /* CLIB_MARCH_VARIANT */
+
+always_inline uword
+vxlan_gbp_encap_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame, u8 is_ip4, u8 csum_offload)
+{
+ u32 n_left_from, next_index, *from, *to_next;
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vnet_main_t *vnm = vxm->vnet_main;
+ vnet_interface_main_t *im = &vnm->interface_main;
+ vlib_combined_counter_main_t *tx_counter =
+ im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX;
+ u32 pkts_encapsulated = 0;
+ u32 thread_index = vlib_get_thread_index ();
+ u32 sw_if_index0 = 0, sw_if_index1 = 0;
+ u32 next0 = 0, next1 = 0;
+ vxlan_gbp_tunnel_t *t0 = NULL, *t1 = NULL;
+ index_t dpoi_idx0 = INDEX_INVALID, dpoi_idx1 = INDEX_INVALID;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
+
+ from = vlib_frame_vector_args (from_frame);
+ n_left_from = from_frame->n_vectors;
+ vlib_get_buffers (vm, from, bufs, n_left_from);
+
+ next_index = node->cached_next_index;
+
+ STATIC_ASSERT_SIZEOF (ip6_vxlan_gbp_header_t, 56);
+ STATIC_ASSERT_SIZEOF (ip4_vxlan_gbp_header_t, 36);
+
+ u8 const underlay_hdr_len = is_ip4 ?
+ sizeof (ip4_vxlan_gbp_header_t) : sizeof (ip6_vxlan_gbp_header_t);
+ u16 const l3_len = is_ip4 ? sizeof (ip4_header_t) : sizeof (ip6_header_t);
+ u32 const csum_flags =
+ is_ip4 ? VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+ VNET_BUFFER_F_L4_HDR_OFFSET_VALID :
+ VNET_BUFFER_F_IS_IP6 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+ VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
+ u32 const outer_packet_csum_offload_flags =
+ is_ip4 ? VNET_BUFFER_OFFLOAD_F_IP_CKSUM | VNET_BUFFER_OFFLOAD_F_UDP_CKSUM :
+ VNET_BUFFER_OFFLOAD_F_UDP_CKSUM;
+ u32 const inner_packet_removed_flags =
+ VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_IS_IP6 |
+ VNET_BUFFER_F_L2_HDR_OFFSET_VALID | VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+ VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
+
+ 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 >= 4 && n_left_to_next >= 2)
+ {
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (b[2]->data - CLIB_CACHE_LINE_BYTES,
+ 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (b[3]->data - CLIB_CACHE_LINE_BYTES,
+ 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ u32 bi0 = to_next[0] = from[0];
+ u32 bi1 = to_next[1] = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+ n_left_from -= 2;
+
+ u32 or_flags = b[0]->flags | b[1]->flags;
+ if (csum_offload && (or_flags & VNET_BUFFER_F_OFFLOAD))
+ {
+ /* Only calculate the non-GSO packet csum offload */
+ if ((b[0]->flags & VNET_BUFFER_F_GSO) == 0)
+ {
+ vnet_calc_checksums_inline (vm, b[0],
+ b[0]->flags &
+ VNET_BUFFER_F_IS_IP4,
+ b[0]->flags &
+ VNET_BUFFER_F_IS_IP6);
+ b[0]->flags &= ~inner_packet_removed_flags;
+ }
+ if ((b[1]->flags & VNET_BUFFER_F_GSO) == 0)
+ {
+ vnet_calc_checksums_inline (vm, b[1],
+ b[1]->flags &
+ VNET_BUFFER_F_IS_IP4,
+ b[1]->flags &
+ VNET_BUFFER_F_IS_IP6);
+ b[1]->flags &= ~inner_packet_removed_flags;
+ }
+ }
+
+ u32 flow_hash0 = vnet_l2_compute_flow_hash (b[0]);
+ u32 flow_hash1 = vnet_l2_compute_flow_hash (b[1]);
+
+ /* Get next node index and adj index from tunnel next_dpo */
+ if (sw_if_index0 != vnet_buffer (b[0])->sw_if_index[VLIB_TX])
+ {
+ sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
+ vnet_hw_interface_t *hi0 =
+ vnet_get_sup_hw_interface (vnm, sw_if_index0);
+ t0 = &vxm->tunnels[hi0->dev_instance];
+ /* Note: change to always set next0 if it may set to drop */
+ next0 = t0->next_dpo.dpoi_next_node;
+ dpoi_idx0 = t0->next_dpo.dpoi_index;
+ }
+
+ /* Get next node index and adj index from tunnel next_dpo */
+ if (sw_if_index1 != vnet_buffer (b[1])->sw_if_index[VLIB_TX])
+ {
+ if (sw_if_index0 == vnet_buffer (b[1])->sw_if_index[VLIB_TX])
+ {
+ sw_if_index1 = sw_if_index0;
+ t1 = t0;
+ next1 = next0;
+ dpoi_idx1 = dpoi_idx0;
+ }
+ else
+ {
+ sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
+ vnet_hw_interface_t *hi1 =
+ vnet_get_sup_hw_interface (vnm, sw_if_index1);
+ t1 = &vxm->tunnels[hi1->dev_instance];
+ /* Note: change to always set next1 if it may set to drop */
+ next1 = t1->next_dpo.dpoi_next_node;
+ dpoi_idx1 = t1->next_dpo.dpoi_index;
+ }
+ }
+
+ vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = dpoi_idx0;
+ vnet_buffer (b[1])->ip.adj_index[VLIB_TX] = dpoi_idx1;
+
+ ASSERT (t0->rewrite_header.data_bytes == underlay_hdr_len);
+ ASSERT (t1->rewrite_header.data_bytes == underlay_hdr_len);
+ vnet_rewrite_two_headers (*t0, *t1, vlib_buffer_get_current (b[0]),
+ vlib_buffer_get_current (b[1]),
+ underlay_hdr_len);
+
+ vlib_buffer_advance (b[0], -underlay_hdr_len);
+ vlib_buffer_advance (b[1], -underlay_hdr_len);
+
+ u32 len0 = vlib_buffer_length_in_chain (vm, b[0]);
+ u32 len1 = vlib_buffer_length_in_chain (vm, b[1]);
+ u16 payload_l0 = clib_host_to_net_u16 (len0 - l3_len);
+ u16 payload_l1 = clib_host_to_net_u16 (len1 - l3_len);
+
+ void *underlay0 = vlib_buffer_get_current (b[0]);
+ void *underlay1 = vlib_buffer_get_current (b[1]);
+
+ ip4_header_t *ip4_0, *ip4_1;
+ qos_bits_t ip4_0_tos = 0, ip4_1_tos = 0;
+ ip6_header_t *ip6_0, *ip6_1;
+ udp_header_t *udp0, *udp1;
+ vxlan_gbp_header_t *vxlan_gbp0, *vxlan_gbp1;
+ u8 *l3_0, *l3_1;
+ if (is_ip4)
+ {
+ ip4_vxlan_gbp_header_t *hdr0 = underlay0;
+ ip4_vxlan_gbp_header_t *hdr1 = underlay1;
+
+ /* Fix the IP4 checksum and length */
+ ip4_0 = &hdr0->ip4;
+ ip4_1 = &hdr1->ip4;
+ ip4_0->length = clib_host_to_net_u16 (len0);
+ ip4_1->length = clib_host_to_net_u16 (len1);
+
+ if (PREDICT_FALSE (b[0]->flags & VNET_BUFFER_F_QOS_DATA_VALID))
+ {
+ ip4_0_tos = vnet_buffer2 (b[0])->qos.bits;
+ ip4_0->tos = ip4_0_tos;
+ }
+ if (PREDICT_FALSE (b[1]->flags & VNET_BUFFER_F_QOS_DATA_VALID))
+ {
+ ip4_1_tos = vnet_buffer2 (b[1])->qos.bits;
+ ip4_1->tos = ip4_1_tos;
+ }
+
+ l3_0 = (u8 *) ip4_0;
+ l3_1 = (u8 *) ip4_1;
+ udp0 = &hdr0->udp;
+ udp1 = &hdr1->udp;
+ vxlan_gbp0 = &hdr0->vxlan_gbp;
+ vxlan_gbp1 = &hdr1->vxlan_gbp;
+ }
+ else /* ipv6 */
+ {
+ ip6_vxlan_gbp_header_t *hdr0 = underlay0;
+ ip6_vxlan_gbp_header_t *hdr1 = underlay1;
+
+ /* Fix IP6 payload length */
+ ip6_0 = &hdr0->ip6;
+ ip6_1 = &hdr1->ip6;
+ ip6_0->payload_length = payload_l0;
+ ip6_1->payload_length = payload_l1;
+
+ l3_0 = (u8 *) ip6_0;
+ l3_1 = (u8 *) ip6_1;
+ udp0 = &hdr0->udp;
+ udp1 = &hdr1->udp;
+ vxlan_gbp0 = &hdr0->vxlan_gbp;
+ vxlan_gbp1 = &hdr1->vxlan_gbp;
+ }
+
+ /* Fix UDP length and set source port */
+ udp0->length = payload_l0;
+ udp0->src_port = flow_hash0;
+ udp1->length = payload_l1;
+ udp1->src_port = flow_hash1;
+
+ /* set source class and gpflags */
+ vxlan_gbp0->gpflags = vnet_buffer2 (b[0])->gbp.flags;
+ vxlan_gbp1->gpflags = vnet_buffer2 (b[1])->gbp.flags;
+ vxlan_gbp0->sclass =
+ clib_host_to_net_u16 (vnet_buffer2 (b[0])->gbp.sclass);
+ vxlan_gbp1->sclass =
+ clib_host_to_net_u16 (vnet_buffer2 (b[1])->gbp.sclass);
+
+ if (csum_offload)
+ {
+ b[0]->flags |= csum_flags;
+ vnet_buffer (b[0])->l3_hdr_offset = l3_0 - b[0]->data;
+ vnet_buffer (b[0])->l4_hdr_offset = (u8 *) udp0 - b[0]->data;
+ vnet_buffer_offload_flags_set (b[0],
+ outer_packet_csum_offload_flags);
+ b[1]->flags |= csum_flags;
+ vnet_buffer (b[1])->l3_hdr_offset = l3_1 - b[1]->data;
+ vnet_buffer (b[1])->l4_hdr_offset = (u8 *) udp1 - b[1]->data;
+ vnet_buffer_offload_flags_set (b[1],
+ outer_packet_csum_offload_flags);
+ }
+ /* IPv4 UDP checksum only if checksum offload is used */
+ else if (is_ip4)
+ {
+ ip_csum_t sum0 = ip4_0->checksum;
+ sum0 = ip_csum_update (sum0, 0, ip4_0->length, ip4_header_t,
+ length /* changed member */ );
+ if (PREDICT_FALSE (ip4_0_tos))
+ {
+ sum0 = ip_csum_update (sum0, 0, ip4_0_tos, ip4_header_t,
+ tos /* changed member */ );
+ }
+ ip4_0->checksum = ip_csum_fold (sum0);
+ ip_csum_t sum1 = ip4_1->checksum;
+ sum1 = ip_csum_update (sum1, 0, ip4_1->length, ip4_header_t,
+ length /* changed member */ );
+ if (PREDICT_FALSE (ip4_1_tos))
+ {
+ sum1 = ip_csum_update (sum1, 0, ip4_1_tos, ip4_header_t,
+ tos /* changed member */ );
+ }
+ ip4_1->checksum = ip_csum_fold (sum1);
+ }
+ /* IPv6 UDP checksum is mandatory */
+ else
+ {
+ int bogus = 0;
+
+ udp0->checksum = ip6_tcp_udp_icmp_compute_checksum
+ (vm, b[0], ip6_0, &bogus);
+ ASSERT (bogus == 0);
+ if (udp0->checksum == 0)
+ udp0->checksum = 0xffff;
+ udp1->checksum = ip6_tcp_udp_icmp_compute_checksum
+ (vm, b[1], ip6_1, &bogus);
+ ASSERT (bogus == 0);
+ if (udp1->checksum == 0)
+ udp1->checksum = 0xffff;
+ }
+
+ /* save inner packet flow_hash for load-balance node */
+ vnet_buffer (b[0])->ip.flow_hash = flow_hash0;
+ vnet_buffer (b[1])->ip.flow_hash = flow_hash1;
+
+ vlib_increment_combined_counter (tx_counter, thread_index,
+ sw_if_index0, 1, len0);
+ vlib_increment_combined_counter (tx_counter, thread_index,
+ sw_if_index1, 1, len1);
+ pkts_encapsulated += 2;
+
+ if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ vxlan_gbp_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b[0], sizeof (*tr));
+ tr->tunnel_index = t0 - vxm->tunnels;
+ tr->vni = t0->vni;
+ tr->sclass = vnet_buffer2 (b[0])->gbp.sclass;
+ tr->flags = vnet_buffer2 (b[0])->gbp.flags;
+ }
+
+ if (PREDICT_FALSE (b[1]->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ vxlan_gbp_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b[1], sizeof (*tr));
+ tr->tunnel_index = t1 - vxm->tunnels;
+ tr->vni = t1->vni;
+ tr->sclass = vnet_buffer2 (b[1])->gbp.sclass;
+ tr->flags = vnet_buffer2 (b[1])->gbp.flags;
+ }
+ b += 2;
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0 = to_next[0] = from[0];
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ if (csum_offload && (b[0]->flags & VNET_BUFFER_F_OFFLOAD))
+ {
+ /* Only calculate the non-GSO packet csum offload */
+ if ((b[0]->flags & VNET_BUFFER_F_GSO) == 0)
+ {
+ vnet_calc_checksums_inline (vm, b[0],
+ b[0]->flags &
+ VNET_BUFFER_F_IS_IP4,
+ b[0]->flags &
+ VNET_BUFFER_F_IS_IP6);
+ b[0]->flags &= ~inner_packet_removed_flags;
+ }
+ }
+
+ u32 flow_hash0 = vnet_l2_compute_flow_hash (b[0]);
+
+ /* Get next node index and adj index from tunnel next_dpo */
+ if (sw_if_index0 != vnet_buffer (b[0])->sw_if_index[VLIB_TX])
+ {
+ sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
+ vnet_hw_interface_t *hi0 =
+ vnet_get_sup_hw_interface (vnm, sw_if_index0);
+ t0 = &vxm->tunnels[hi0->dev_instance];
+ /* Note: change to always set next0 if it may be set to drop */
+ next0 = t0->next_dpo.dpoi_next_node;
+ dpoi_idx0 = t0->next_dpo.dpoi_index;
+ }
+ vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = dpoi_idx0;
+
+ ASSERT (t0->rewrite_header.data_bytes == underlay_hdr_len);
+ vnet_rewrite_one_header (*t0, vlib_buffer_get_current (b[0]),
+ underlay_hdr_len);
+
+ vlib_buffer_advance (b[0], -underlay_hdr_len);
+ void *underlay0 = vlib_buffer_get_current (b[0]);
+
+ u32 len0 = vlib_buffer_length_in_chain (vm, b[0]);
+ u16 payload_l0 = clib_host_to_net_u16 (len0 - l3_len);
+
+ vxlan_gbp_header_t *vxlan_gbp0;
+ udp_header_t *udp0;
+ ip4_header_t *ip4_0;
+ qos_bits_t ip4_0_tos = 0;
+ ip6_header_t *ip6_0;
+ u8 *l3_0;
+ if (is_ip4)
+ {
+ ip4_vxlan_gbp_header_t *hdr = underlay0;
+
+ /* Fix the IP4 checksum and length */
+ ip4_0 = &hdr->ip4;
+ ip4_0->length = clib_host_to_net_u16 (len0);
+
+ if (PREDICT_FALSE (b[0]->flags & VNET_BUFFER_F_QOS_DATA_VALID))
+ {
+ ip4_0_tos = vnet_buffer2 (b[0])->qos.bits;
+ ip4_0->tos = ip4_0_tos;
+ }
+
+ l3_0 = (u8 *) ip4_0;
+ udp0 = &hdr->udp;
+ vxlan_gbp0 = &hdr->vxlan_gbp;
+ }
+ else /* ip6 path */
+ {
+ ip6_vxlan_gbp_header_t *hdr = underlay0;
+
+ /* Fix IP6 payload length */
+ ip6_0 = &hdr->ip6;
+ ip6_0->payload_length = payload_l0;
+
+ l3_0 = (u8 *) ip6_0;
+ udp0 = &hdr->udp;
+ vxlan_gbp0 = &hdr->vxlan_gbp;
+ }
+
+ /* Fix UDP length and set source port */
+ udp0->length = payload_l0;
+ udp0->src_port = flow_hash0;
+
+ /* set source class and gpflags */
+ vxlan_gbp0->gpflags = vnet_buffer2 (b[0])->gbp.flags;
+ vxlan_gbp0->sclass =
+ clib_host_to_net_u16 (vnet_buffer2 (b[0])->gbp.sclass);
+
+ if (csum_offload)
+ {
+ b[0]->flags |= csum_flags;
+ vnet_buffer (b[0])->l3_hdr_offset = l3_0 - b[0]->data;
+ vnet_buffer (b[0])->l4_hdr_offset = (u8 *) udp0 - b[0]->data;
+ vnet_buffer_offload_flags_set (b[0],
+ outer_packet_csum_offload_flags);
+ }
+ /* IPv4 UDP checksum only if checksum offload is used */
+ else if (is_ip4)
+ {
+ ip_csum_t sum0 = ip4_0->checksum;
+ sum0 = ip_csum_update (sum0, 0, ip4_0->length, ip4_header_t,
+ length /* changed member */ );
+ if (PREDICT_FALSE (ip4_0_tos))
+ {
+ sum0 = ip_csum_update (sum0, 0, ip4_0_tos, ip4_header_t,
+ tos /* changed member */ );
+ }
+ ip4_0->checksum = ip_csum_fold (sum0);
+ }
+ /* IPv6 UDP checksum is mandatory */
+ else
+ {
+ int bogus = 0;
+
+ udp0->checksum = ip6_tcp_udp_icmp_compute_checksum
+ (vm, b[0], ip6_0, &bogus);
+ ASSERT (bogus == 0);
+ if (udp0->checksum == 0)
+ udp0->checksum = 0xffff;
+ }
+
+ /* save inner packet flow_hash for load-balance node */
+ vnet_buffer (b[0])->ip.flow_hash = flow_hash0;
+
+ vlib_increment_combined_counter (tx_counter, thread_index,
+ sw_if_index0, 1, len0);
+ pkts_encapsulated++;
+
+ if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ vxlan_gbp_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b[0], sizeof (*tr));
+ tr->tunnel_index = t0 - vxm->tunnels;
+ tr->vni = t0->vni;
+ tr->sclass = vnet_buffer2 (b[0])->gbp.sclass;
+ tr->flags = vnet_buffer2 (b[0])->gbp.flags;
+ }
+ b += 1;
+
+ 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);
+ }
+
+ /* Do we still need this now that tunnel tx stats is kept? */
+ vlib_node_increment_counter (vm, node->node_index,
+ VXLAN_GBP_ENCAP_ERROR_ENCAPSULATED,
+ pkts_encapsulated);
+
+ return from_frame->n_vectors;
+}
+
+VLIB_NODE_FN (vxlan4_gbp_encap_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ /* Disable chksum offload as setup overhead in tx node is not worthwhile
+ for ip4 header checksum only, unless udp checksum is also required */
+ return vxlan_gbp_encap_inline (vm, node, from_frame, /* is_ip4 */ 1,
+ /* csum_offload */ 0);
+}
+
+VLIB_NODE_FN (vxlan6_gbp_encap_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
+{
+ /* Enable checksum offload for ip6 as udp checksum is mandatory, */
+ return vxlan_gbp_encap_inline (vm, node, from_frame, /* is_ip4 */ 0,
+ /* csum_offload */ 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (vxlan4_gbp_encap_node) =
+{
+ .name = "vxlan4-gbp-encap",
+ .vector_size = sizeof (u32),
+ .format_trace = format_vxlan_gbp_encap_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (vxlan_gbp_encap_error_strings),
+ .error_strings = vxlan_gbp_encap_error_strings,
+ .n_next_nodes = VXLAN_GBP_ENCAP_N_NEXT,
+ .next_nodes = {
+ [VXLAN_GBP_ENCAP_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (vxlan6_gbp_encap_node) =
+{
+ .name = "vxlan6-gbp-encap",
+ .vector_size = sizeof (u32),
+ .format_trace = format_vxlan_gbp_encap_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (vxlan_gbp_encap_error_strings),
+ .error_strings = vxlan_gbp_encap_error_strings,
+ .n_next_nodes = VXLAN_GBP_ENCAP_N_NEXT,
+ .next_nodes = {
+ [VXLAN_GBP_ENCAP_NEXT_DROP] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py b/extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py
new file mode 100644
index 00000000000..1d64937b6dd
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+
+import socket
+from util import ip4_range, reassemble4_ether
+import unittest
+from framework import VppTestCase, VppTestRunner
+from template_bd import BridgeDomain
+
+from scapy.layers.l2 import Ether
+from scapy.packet import Raw
+from scapy.layers.inet import IP, UDP
+from scapy.layers.vxlan import VXLAN
+
+from vpp_ip_route import VppIpRoute, VppRoutePath
+from vpp_ip import INVALID_INDEX
+
+
+class TestVxlanGbp(VppTestCase):
+ """VXLAN GBP Test Case"""
+
+ @property
+ def frame_request(self):
+ """Ethernet frame modeling a generic request"""
+ return (
+ Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")
+ / IP(src="1.2.3.4", dst="4.3.2.1")
+ / UDP(sport=10000, dport=20000)
+ / Raw(b"\xa5" * 100)
+ )
+
+ @property
+ def frame_reply(self):
+ """Ethernet frame modeling a generic reply"""
+ return (
+ Ether(src="00:00:00:00:00:02", dst="00:00:00:00:00:01")
+ / IP(src="4.3.2.1", dst="1.2.3.4")
+ / UDP(sport=20000, dport=10000)
+ / Raw(b"\xa5" * 100)
+ )
+
+ def encapsulate(self, pkt, vni):
+ """
+ Encapsulate the original payload frame by adding VXLAN GBP header with
+ its UDP, IP and Ethernet fields
+ """
+ return (
+ Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
+ / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
+ / UDP(sport=self.dport, dport=self.dport, chksum=0)
+ / VXLAN(vni=vni, flags=self.flags, gpflags=self.gpflags, gpid=self.sclass)
+ / pkt
+ )
+
+ def ip_range(self, start, end):
+ """range of remote ip's"""
+ return ip4_range(self.pg0.remote_ip4, start, end)
+
+ def decapsulate(self, pkt):
+ """
+ Decapsulate the original payload frame by removing VXLAN header
+ """
+ # check if is set G and I flag
+ self.assertEqual(pkt[VXLAN].flags, int("0x88", 16))
+ return pkt[VXLAN].payload
+
+ # Method for checking VXLAN GBP encapsulation.
+ #
+ def check_encapsulation(self, pkt, vni, local_only=False, mcast_pkt=False):
+ # TODO: add error messages
+ # Verify source MAC is VPP_MAC and destination MAC is MY_MAC resolved
+ # by VPP using ARP.
+ self.assertEqual(pkt[Ether].src, self.pg0.local_mac)
+ if not local_only:
+ if not mcast_pkt:
+ self.assertEqual(pkt[Ether].dst, self.pg0.remote_mac)
+ else:
+ self.assertEqual(pkt[Ether].dst, type(self).mcast_mac)
+ # Verify VXLAN GBP tunnel source IP is VPP_IP and destination IP is
+ # MY_IP.
+ self.assertEqual(pkt[IP].src, self.pg0.local_ip4)
+ if not local_only:
+ if not mcast_pkt:
+ self.assertEqual(pkt[IP].dst, self.pg0.remote_ip4)
+ else:
+ self.assertEqual(pkt[IP].dst, type(self).mcast_ip4)
+ # Verify UDP destination port is VXLAN GBP 48879, source UDP port could
+ # be arbitrary.
+ self.assertEqual(pkt[UDP].dport, type(self).dport)
+ # Verify UDP checksum
+ self.assert_udp_checksum_valid(pkt)
+ # Verify VNI
+ # pkt.show()
+ self.assertEqual(pkt[VXLAN].vni, vni)
+ # Verify Source Class
+ self.assertEqual(pkt[VXLAN].gpid, 0)
+
+ @classmethod
+ def create_vxlan_gbp_flood_test_bd(cls, vni, n_ucast_tunnels):
+ # Create 2 ucast vxlan tunnels under bd
+ ip_range_start = 10
+ ip_range_end = ip_range_start + n_ucast_tunnels
+ next_hop_address = cls.pg0.remote_ip4
+ for dest_ip4 in ip4_range(cls.pg0.remote_ip4, ip_range_start, ip_range_end):
+ # add host route so dest_ip4 will not be resolved
+ rip = VppIpRoute(
+ cls,
+ dest_ip4,
+ 32,
+ [VppRoutePath(next_hop_address, INVALID_INDEX)],
+ register=False,
+ )
+ rip.add_vpp_config()
+ r = cls.vapi.vxlan_gbp_tunnel_add_del(
+ tunnel={
+ "src": cls.pg0.local_ip4,
+ "dst": dest_ip4,
+ "vni": vni,
+ "instance": INVALID_INDEX,
+ "mcast_sw_if_index": INVALID_INDEX,
+ "mode": 1,
+ },
+ is_add=1,
+ )
+ cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, bd_id=vni)
+
+ # Class method to start the VXLAN GBP test case.
+ # Overrides setUpClass method in VppTestCase class.
+ # Python try..except statement is used to ensure that the tear down of
+ # the class will be executed even if exception is raised.
+ # @param cls The class pointer.
+ @classmethod
+ def setUpClass(cls):
+ super(TestVxlanGbp, cls).setUpClass()
+
+ try:
+ cls.dport = 48879
+ cls.flags = 0x88
+ cls.gpflags = 0x0
+ cls.sclass = 0
+
+ # Create 2 pg interfaces.
+ cls.create_pg_interfaces(range(4))
+ for pg in cls.pg_interfaces:
+ pg.admin_up()
+
+ # Configure IPv4 addresses on VPP pg0.
+ cls.pg0.config_ip4()
+
+ # Resolve MAC address for VPP's IP address on pg0.
+ cls.pg0.resolve_arp()
+
+ # Create VXLAN GBP VTEP on VPP pg0, and put vxlan_gbp_tunnel0 and
+ # pg1 into BD.
+ cls.single_tunnel_bd = 1
+ cls.single_tunnel_vni = 0xABCDE
+ r = cls.vapi.vxlan_gbp_tunnel_add_del(
+ tunnel={
+ "src": cls.pg0.local_ip4,
+ "dst": cls.pg0.remote_ip4,
+ "vni": cls.single_tunnel_vni,
+ "instance": INVALID_INDEX,
+ "mcast_sw_if_index": INVALID_INDEX,
+ "mode": 1,
+ },
+ is_add=1,
+ )
+ cls.vapi.sw_interface_set_l2_bridge(
+ rx_sw_if_index=r.sw_if_index, bd_id=cls.single_tunnel_bd
+ )
+ cls.vapi.sw_interface_set_l2_bridge(
+ rx_sw_if_index=cls.pg1.sw_if_index, bd_id=cls.single_tunnel_bd
+ )
+
+ # Setup vni 2 to test multicast flooding
+ cls.n_ucast_tunnels = 2
+ # Setup vni 3 to test unicast flooding
+ cls.ucast_flood_bd = 3
+ cls.create_vxlan_gbp_flood_test_bd(cls.ucast_flood_bd, cls.n_ucast_tunnels)
+ cls.vapi.sw_interface_set_l2_bridge(
+ rx_sw_if_index=cls.pg3.sw_if_index, bd_id=cls.ucast_flood_bd
+ )
+ except Exception:
+ super(TestVxlanGbp, cls).tearDownClass()
+ raise
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestVxlanGbp, cls).tearDownClass()
+
+ def assert_eq_pkts(self, pkt1, pkt2):
+ """Verify the Ether, IP, UDP, payload are equal in both
+ packets
+ """
+ self.assertEqual(pkt1[Ether].src, pkt2[Ether].src)
+ self.assertEqual(pkt1[Ether].dst, pkt2[Ether].dst)
+ self.assertEqual(pkt1[IP].src, pkt2[IP].src)
+ self.assertEqual(pkt1[IP].dst, pkt2[IP].dst)
+ self.assertEqual(pkt1[UDP].sport, pkt2[UDP].sport)
+ self.assertEqual(pkt1[UDP].dport, pkt2[UDP].dport)
+ self.assertEqual(pkt1[Raw], pkt2[Raw])
+
+ def test_decap(self):
+ """Decapsulation test
+ Send encapsulated frames from pg0
+ Verify receipt of decapsulated frames on pg1
+ """
+ encapsulated_pkt = self.encapsulate(self.frame_request, self.single_tunnel_vni)
+
+ self.pg0.add_stream(
+ [
+ encapsulated_pkt,
+ ]
+ )
+
+ self.pg1.enable_capture()
+
+ self.pg_start()
+
+ # Pick first received frame and check if it's the non-encapsulated
+ # frame
+ out = self.pg1.get_capture(1)
+ pkt = out[0]
+ self.assert_eq_pkts(pkt, self.frame_request)
+
+ def test_encap(self):
+ """Encapsulation test
+ Send frames from pg1
+ Verify receipt of encapsulated frames on pg0
+ """
+ self.pg1.add_stream([self.frame_reply])
+
+ self.pg0.enable_capture()
+
+ self.pg_start()
+
+ # Pick first received frame and check if it's correctly encapsulated.
+ out = self.pg0.get_capture(1)
+ pkt = out[0]
+ self.check_encapsulation(pkt, self.single_tunnel_vni)
+
+ payload = self.decapsulate(pkt)
+ self.assert_eq_pkts(payload, self.frame_reply)
+
+ def test_ucast_flood(self):
+ """Unicast flood test
+ Send frames from pg3
+ Verify receipt of encapsulated frames on pg0
+ """
+ self.pg3.add_stream([self.frame_reply])
+
+ self.pg0.enable_capture()
+
+ self.pg_start()
+
+ # Get packet from each tunnel and assert it's correctly encapsulated.
+ out = self.pg0.get_capture(self.n_ucast_tunnels)
+ for pkt in out:
+ self.check_encapsulation(pkt, self.ucast_flood_bd, True)
+ payload = self.decapsulate(pkt)
+ self.assert_eq_pkts(payload, self.frame_reply)
+
+ def test_encap_big_packet(self):
+ """Encapsulation test send big frame from pg1
+ Verify receipt of encapsulated frames on pg0
+ """
+
+ self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1500, 0, 0, 0])
+
+ frame = (
+ Ether(src="00:00:00:00:00:02", dst="00:00:00:00:00:01")
+ / IP(src="4.3.2.1", dst="1.2.3.4")
+ / UDP(sport=20000, dport=10000)
+ / Raw(b"\xa5" * 1450)
+ )
+
+ self.pg1.add_stream([frame])
+
+ self.pg0.enable_capture()
+
+ self.pg_start()
+
+ # Pick first received frame and check if it's correctly encapsulated.
+ out = self.pg0.get_capture(2)
+ pkt = reassemble4_ether(out)
+ self.check_encapsulation(pkt, self.single_tunnel_vni)
+
+ payload = self.decapsulate(pkt)
+ self.assert_eq_pkts(payload, frame)
+
+ # Method to define VPP actions before tear down of the test case.
+ # Overrides tearDown method in VppTestCase class.
+ # @param self The object pointer.
+ def tearDown(self):
+ super(TestVxlanGbp, self).tearDown()
+
+ def show_commands_at_teardown(self):
+ self.logger.info(self.vapi.cli("show bridge-domain 1 detail"))
+ self.logger.info(self.vapi.cli("show bridge-domain 3 detail"))
+ self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+ self.logger.info(self.vapi.cli("show error"))
+
+
+if __name__ == "__main__":
+ unittest.main(testRunner=VppTestRunner)
diff --git a/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py b/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py
new file mode 100644
index 00000000000..2a373f8e56e
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py
@@ -0,0 +1,91 @@
+from vpp_interface import VppInterface
+from vpp_papi import VppEnum
+
+
+INDEX_INVALID = 0xFFFFFFFF
+
+
+def find_vxlan_gbp_tunnel(test, src, dst, vni):
+ ts = test.vapi.vxlan_gbp_tunnel_dump(INDEX_INVALID)
+ for t in ts:
+ if (
+ src == str(t.tunnel.src)
+ and dst == str(t.tunnel.dst)
+ and t.tunnel.vni == vni
+ ):
+ return t.tunnel.sw_if_index
+ return INDEX_INVALID
+
+
+class VppVxlanGbpTunnel(VppInterface):
+ """
+ VPP VXLAN GBP interface
+ """
+
+ def __init__(
+ self,
+ test,
+ src,
+ dst,
+ vni,
+ mcast_itf=None,
+ mode=None,
+ is_ipv6=None,
+ encap_table_id=None,
+ instance=0xFFFFFFFF,
+ ):
+ """Create VXLAN-GBP Tunnel interface"""
+ super(VppVxlanGbpTunnel, self).__init__(test)
+ self.src = src
+ self.dst = dst
+ self.vni = vni
+ self.mcast_itf = mcast_itf
+ self.ipv6 = is_ipv6
+ self.encap_table_id = encap_table_id
+ self.instance = instance
+ if not mode:
+ self.mode = (
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L2
+ )
+ else:
+ self.mode = mode
+
+ def encode(self):
+ return {
+ "src": self.src,
+ "dst": self.dst,
+ "mode": self.mode,
+ "vni": self.vni,
+ "mcast_sw_if_index": (
+ self.mcast_itf.sw_if_index if self.mcast_itf else INDEX_INVALID
+ ),
+ "encap_table_id": self.encap_table_id,
+ "instance": self.instance,
+ }
+
+ def add_vpp_config(self):
+ reply = self.test.vapi.vxlan_gbp_tunnel_add_del(
+ is_add=1,
+ tunnel=self.encode(),
+ )
+ self.set_sw_if_index(reply.sw_if_index)
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self.test.vapi.vxlan_gbp_tunnel_add_del(
+ is_add=0,
+ tunnel=self.encode(),
+ )
+
+ def query_vpp_config(self):
+ return INDEX_INVALID != find_vxlan_gbp_tunnel(
+ self._test, self.src, self.dst, self.vni
+ )
+
+ def object_id(self):
+ return "vxlan-gbp-%d-%d-%s-%s" % (
+ self.sw_if_index,
+ self.vni,
+ self.src,
+ self.dst,
+ )
diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api
new file mode 100644
index 00000000000..68566697000
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api
@@ -0,0 +1,100 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * 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.
+ */
+
+option version = "1.1.1";
+import "vnet/ip/ip_types.api";
+import "vnet/interface_types.api";
+
+enum vxlan_gbp_api_tunnel_mode
+{
+ VXLAN_GBP_API_TUNNEL_MODE_L2,
+ VXLAN_GBP_API_TUNNEL_MODE_L3,
+};
+
+/** \brief Definition of a VXLAN GBP tunnel
+ @param instance - optional unique custom device instance, else ~0.
+ @param src - Source IP address
+ @param dst - Destination IP address, can be multicast
+ @param mcast_sw_if_index - Interface for multicast destination
+ @param encap_table_id - Encap route table
+ @param vni - The VXLAN Network Identifier, uint24
+ @param sw_ifindex - Ignored in add message, set in details
+*/
+typedef vxlan_gbp_tunnel
+{
+ u32 instance;
+ vl_api_address_t src;
+ vl_api_address_t dst;
+ vl_api_interface_index_t mcast_sw_if_index;
+ u32 encap_table_id;
+ u32 vni;
+ vl_api_interface_index_t sw_if_index;
+ vl_api_vxlan_gbp_api_tunnel_mode_t mode;
+};
+
+/** \brief Create or delete a VXLAN-GBP tunnel
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_add - Use 1 to create the tunnel, 0 to remove it
+*/
+define vxlan_gbp_tunnel_add_del
+{
+ u32 client_index;
+ u32 context;
+ bool is_add [default=true];
+ vl_api_vxlan_gbp_tunnel_t tunnel;
+ option in_progress;
+};
+
+define vxlan_gbp_tunnel_add_del_reply
+{
+ u32 context;
+ i32 retval;
+ vl_api_interface_index_t sw_if_index;
+ option in_progress;
+};
+
+define vxlan_gbp_tunnel_dump
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index [default=0xffffffff];
+ option in_progress;
+};
+
+define vxlan_gbp_tunnel_details
+{
+ u32 context;
+ vl_api_vxlan_gbp_tunnel_t tunnel;
+ option in_progress;
+};
+
+/** \brief Interface set vxlan-bypass request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - interface used to reach neighbor
+ @param is_ipv6 - if non-zero, enable ipv6-vxlan-bypass, else ipv4-vxlan-bypass
+ @param enable - if non-zero enable, else disable
+*/
+autoreply define sw_interface_set_vxlan_gbp_bypass
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+ bool is_ipv6;
+ bool enable [default=true];
+ option in_progress;
+};
diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c
new file mode 100644
index 00000000000..eb685b8a40c
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c
@@ -0,0 +1,1193 @@
+/*
+ * 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 <vnet/vxlan-gbp/vxlan_gbp.h>
+#include <vnet/ip/format.h>
+#include <vnet/ip/punt.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_entry_track.h>
+#include <vnet/mfib/mfib_table.h>
+#include <vnet/adj/adj_mcast.h>
+#include <vnet/adj/rewrite.h>
+#include <vnet/interface.h>
+#include <vlib/vlib.h>
+
+/**
+ * @file
+ * @brief VXLAN GBP.
+ *
+ * VXLAN GBP provides the features of vxlan and carry group policy id.
+ */
+static vlib_punt_hdl_t punt_hdl;
+
+vxlan_gbp_main_t vxlan_gbp_main;
+
+u8 *
+format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args)
+{
+ vxlan_gbp_tunnel_mode_t mode = va_arg (*args, vxlan_gbp_tunnel_mode_t);
+
+ switch (mode)
+ {
+ case VXLAN_GBP_TUNNEL_MODE_L2:
+ s = format (s, "L2");
+ break;
+ case VXLAN_GBP_TUNNEL_MODE_L3:
+ s = format (s, "L3");
+ break;
+ }
+ return (s);
+}
+
+u8 *
+format_vxlan_gbp_tunnel (u8 * s, va_list * args)
+{
+ vxlan_gbp_tunnel_t *t = va_arg (*args, vxlan_gbp_tunnel_t *);
+
+ s = format (s,
+ "[%d] instance %d src %U dst %U vni %d fib-idx %d"
+ " sw-if-idx %d mode %U ",
+ t->dev_instance, t->user_instance,
+ format_ip46_address, &t->src, IP46_TYPE_ANY,
+ format_ip46_address, &t->dst, IP46_TYPE_ANY,
+ t->vni, t->encap_fib_index, t->sw_if_index,
+ format_vxlan_gbp_tunnel_mode, t->mode);
+
+ s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
+
+ if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
+ s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
+
+ return s;
+}
+
+static u8 *
+format_vxlan_gbp_name (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vxlan_gbp_tunnel_t *t;
+
+ if (dev_instance == ~0)
+ return format (s, "<cached-unused>");
+
+ if (dev_instance >= vec_len (vxm->tunnels))
+ return format (s, "<improperly-referenced>");
+
+ t = pool_elt_at_index (vxm->tunnels, dev_instance);
+
+ return format (s, "vxlan_gbp_tunnel%d", t->user_instance);
+}
+
+static clib_error_t *
+vxlan_gbp_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
+ u32 flags)
+{
+ u32 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 /* no error */ 0;
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (vxlan_gbp_device_class, static) = {
+ .name = "VXLAN-GBP",
+ .format_device_name = format_vxlan_gbp_name,
+ .format_tx_trace = format_vxlan_gbp_encap_trace,
+ .admin_up_down_function = vxlan_gbp_interface_admin_up_down,
+};
+/* *INDENT-ON* */
+
+static u8 *
+format_vxlan_gbp_header_with_length (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ s = format (s, "unimplemented dev %u", dev_instance);
+ return s;
+}
+
+/* *INDENT-OFF* */
+VNET_HW_INTERFACE_CLASS (vxlan_gbp_hw_class) = {
+ .name = "VXLAN-GBP",
+ .format_header = format_vxlan_gbp_header_with_length,
+ .build_rewrite = default_build_rewrite,
+};
+/* *INDENT-ON* */
+
+static void
+vxlan_gbp_tunnel_restack_dpo (vxlan_gbp_tunnel_t * t)
+{
+ u8 is_ip4 = ip46_address_is_ip4 (&t->dst);
+ dpo_id_t dpo = DPO_INVALID;
+ fib_forward_chain_type_t forw_type = is_ip4 ?
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
+
+ fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo);
+
+ /* vxlan_gbp uses the payload hash as the udp source port
+ * hence the packet's hash is unknown
+ * skip single bucket load balance dpo's */
+ while (DPO_LOAD_BALANCE == dpo.dpoi_type)
+ {
+ load_balance_t *lb = load_balance_get (dpo.dpoi_index);
+ if (lb->lb_n_buckets > 1)
+ break;
+
+ dpo_copy (&dpo, load_balance_get_bucket_i (lb, 0));
+ }
+
+ u32 encap_index = is_ip4 ?
+ vxlan4_gbp_encap_node.index : vxlan6_gbp_encap_node.index;
+ dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
+ dpo_reset (&dpo);
+}
+
+static vxlan_gbp_tunnel_t *
+vxlan_gbp_tunnel_from_fib_node (fib_node_t * node)
+{
+ ASSERT (FIB_NODE_TYPE_VXLAN_GBP_TUNNEL == node->fn_type);
+ return ((vxlan_gbp_tunnel_t *) (((char *) node) -
+ STRUCT_OFFSET_OF (vxlan_gbp_tunnel_t,
+ node)));
+}
+
+/**
+ * Function definition to backwalk a FIB node -
+ * Here we will restack the new dpo of VXLAN DIP to encap node.
+ */
+static fib_node_back_walk_rc_t
+vxlan_gbp_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
+{
+ vxlan_gbp_tunnel_restack_dpo (vxlan_gbp_tunnel_from_fib_node (node));
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/**
+ * Function definition to get a FIB node from its index
+ */
+static fib_node_t *
+vxlan_gbp_tunnel_fib_node_get (fib_node_index_t index)
+{
+ vxlan_gbp_tunnel_t *t;
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+
+ t = pool_elt_at_index (vxm->tunnels, index);
+
+ return (&t->node);
+}
+
+/**
+ * Function definition to inform the FIB node that its last lock has gone.
+ */
+static void
+vxlan_gbp_tunnel_last_lock_gone (fib_node_t * node)
+{
+ /*
+ * The VXLAN GBP tunnel is a root of the graph. As such
+ * it never has children and thus is never locked.
+ */
+ ASSERT (0);
+}
+
+/*
+ * Virtual function table registered by VXLAN GBP tunnels
+ * for participation in the FIB object graph.
+ */
+const static fib_node_vft_t vxlan_gbp_vft = {
+ .fnv_get = vxlan_gbp_tunnel_fib_node_get,
+ .fnv_last_lock = vxlan_gbp_tunnel_last_lock_gone,
+ .fnv_back_walk = vxlan_gbp_tunnel_back_walk,
+};
+
+
+#define foreach_copy_field \
+_(vni) \
+_(mode) \
+_(mcast_sw_if_index) \
+_(encap_fib_index) \
+_(src) \
+_(dst)
+
+static void
+vxlan_gbp_rewrite (vxlan_gbp_tunnel_t * t, bool is_ip6)
+{
+ union
+ {
+ ip4_vxlan_gbp_header_t h4;
+ ip6_vxlan_gbp_header_t h6;
+ } h;
+ int len = is_ip6 ? sizeof h.h6 : sizeof h.h4;
+
+ udp_header_t *udp;
+ vxlan_gbp_header_t *vxlan_gbp;
+ /* Fixed portion of the (outer) ip header */
+
+ clib_memset (&h, 0, sizeof (h));
+ if (!is_ip6)
+ {
+ ip4_header_t *ip = &h.h4.ip4;
+ udp = &h.h4.udp, vxlan_gbp = &h.h4.vxlan_gbp;
+ ip->ip_version_and_header_length = 0x45;
+ ip->ttl = 254;
+ ip->protocol = IP_PROTOCOL_UDP;
+
+ ip->src_address = t->src.ip4;
+ ip->dst_address = t->dst.ip4;
+
+ /* we fix up the ip4 header length and checksum after-the-fact */
+ ip->checksum = ip4_header_checksum (ip);
+ }
+ else
+ {
+ ip6_header_t *ip = &h.h6.ip6;
+ udp = &h.h6.udp, vxlan_gbp = &h.h6.vxlan_gbp;
+ ip->ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 (6 << 28);
+ ip->hop_limit = 255;
+ ip->protocol = IP_PROTOCOL_UDP;
+
+ ip->src_address = t->src.ip6;
+ ip->dst_address = t->dst.ip6;
+ }
+
+ /* UDP header, randomize src port on something, maybe? */
+ udp->src_port = clib_host_to_net_u16 (47789);
+ udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp);
+
+ /* VXLAN header */
+ vxlan_gbp_set_header (vxlan_gbp, t->vni);
+ vnet_rewrite_set_data (*t, &h, len);
+}
+
+static uword
+vtep_addr_ref (ip46_address_t * ip)
+{
+ uword *vtep = ip46_address_is_ip4 (ip) ?
+ hash_get (vxlan_gbp_main.vtep4, ip->ip4.as_u32) :
+ hash_get_mem (vxlan_gbp_main.vtep6, &ip->ip6);
+ if (vtep)
+ return ++(*vtep);
+ ip46_address_is_ip4 (ip) ?
+ hash_set (vxlan_gbp_main.vtep4, ip->ip4.as_u32, 1) :
+ hash_set_mem_alloc (&vxlan_gbp_main.vtep6, &ip->ip6, 1);
+ return 1;
+}
+
+static uword
+vtep_addr_unref (ip46_address_t * ip)
+{
+ uword *vtep = ip46_address_is_ip4 (ip) ?
+ hash_get (vxlan_gbp_main.vtep4, ip->ip4.as_u32) :
+ hash_get_mem (vxlan_gbp_main.vtep6, &ip->ip6);
+ ALWAYS_ASSERT (vtep);
+ if (--(*vtep) != 0)
+ return *vtep;
+ ip46_address_is_ip4 (ip) ?
+ hash_unset (vxlan_gbp_main.vtep4, ip->ip4.as_u32) :
+ hash_unset_mem_free (&vxlan_gbp_main.vtep6, &ip->ip6);
+ return 0;
+}
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED(union
+{
+ struct
+ {
+ fib_node_index_t mfib_entry_index;
+ adj_index_t mcast_adj_index;
+ };
+ u64 as_u64;
+}) mcast_shared_t;
+/* *INDENT-ON* */
+
+static inline mcast_shared_t
+mcast_shared_get (ip46_address_t * ip)
+{
+ ASSERT (ip46_address_is_multicast (ip));
+ uword *p = hash_get_mem (vxlan_gbp_main.mcast_shared, ip);
+ ALWAYS_ASSERT (p);
+ mcast_shared_t ret = {.as_u64 = *p };
+ return ret;
+}
+
+static inline void
+mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai)
+{
+ mcast_shared_t new_ep = {
+ .mcast_adj_index = ai,
+ .mfib_entry_index = mfei,
+ };
+
+ hash_set_mem_alloc (&vxlan_gbp_main.mcast_shared, dst, new_ep.as_u64);
+}
+
+static inline void
+mcast_shared_remove (ip46_address_t * dst)
+{
+ mcast_shared_t ep = mcast_shared_get (dst);
+
+ adj_unlock (ep.mcast_adj_index);
+ mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_VXLAN_GBP);
+
+ hash_unset_mem_free (&vxlan_gbp_main.mcast_shared, dst);
+}
+
+inline void
+vxlan_gbp_register_udp_ports (void)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+
+ if (vxm->udp_ports_registered == 0)
+ {
+ udp_register_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan_gbp,
+ vxlan4_gbp_input_node.index, /* is_ip4 */ 1);
+ udp_register_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan6_gbp,
+ vxlan6_gbp_input_node.index, /* is_ip4 */ 0);
+ }
+ /*
+ * Counts the number of vxlan_gbp tunnels
+ */
+ vxm->udp_ports_registered += 1;
+}
+
+inline void
+vxlan_gbp_unregister_udp_ports (void)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+
+ ASSERT (vxm->udp_ports_registered != 0);
+
+ if (vxm->udp_ports_registered == 1)
+ {
+ udp_unregister_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan_gbp,
+ /* is_ip4 */ 1);
+ udp_unregister_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan6_gbp,
+ /* is_ip4 */ 0);
+ }
+
+ vxm->udp_ports_registered -= 1;
+}
+
+int vnet_vxlan_gbp_tunnel_add_del
+ (vnet_vxlan_gbp_tunnel_add_del_args_t * a, u32 * sw_if_indexp)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vxlan_gbp_tunnel_t *t = 0;
+ vnet_main_t *vnm = vxm->vnet_main;
+ u64 *p;
+ u32 sw_if_index = ~0;
+ vxlan4_gbp_tunnel_key_t key4;
+ vxlan6_gbp_tunnel_key_t key6;
+ u32 is_ip6 = a->is_ip6;
+
+ int not_found;
+ if (!is_ip6)
+ {
+ key4.key[0] = ip46_address_is_multicast (&a->dst) ?
+ a->dst.ip4.as_u32 :
+ a->dst.ip4.as_u32 | (((u64) a->src.ip4.as_u32) << 32);
+ key4.key[1] = (((u64) a->encap_fib_index) << 32)
+ | clib_host_to_net_u32 (a->vni << 8);
+ not_found =
+ clib_bihash_search_inline_16_8 (&vxm->vxlan4_gbp_tunnel_by_key,
+ &key4);
+ p = &key4.value;
+ }
+ else
+ {
+ key6.key[0] = a->dst.ip6.as_u64[0];
+ key6.key[1] = a->dst.ip6.as_u64[1];
+ key6.key[2] = (((u64) a->encap_fib_index) << 32)
+ | clib_host_to_net_u32 (a->vni << 8);
+ not_found =
+ clib_bihash_search_inline_24_8 (&vxm->vxlan6_gbp_tunnel_by_key,
+ &key6);
+ p = &key6.value;
+ }
+
+ if (not_found)
+ p = 0;
+
+ if (a->is_add)
+ {
+ l2input_main_t *l2im = &l2input_main;
+ u32 dev_instance; /* real dev instance tunnel index */
+ u32 user_instance; /* request and actual instance number */
+
+ /* adding a tunnel: tunnel must not already exist */
+ if (p)
+ {
+ t = pool_elt_at_index (vxm->tunnels, *p);
+ *sw_if_indexp = t->sw_if_index;
+ return VNET_API_ERROR_TUNNEL_EXIST;
+ }
+ pool_get_aligned (vxm->tunnels, t, CLIB_CACHE_LINE_BYTES);
+ clib_memset (t, 0, sizeof (*t));
+ dev_instance = t - vxm->tunnels;
+
+ /* copy from arg structure */
+#define _(x) t->x = a->x;
+ foreach_copy_field;
+#undef _
+
+ vxlan_gbp_rewrite (t, is_ip6);
+ /*
+ * Reconcile the real dev_instance and a possible requested instance.
+ */
+ user_instance = a->instance;
+ if (user_instance == ~0)
+ user_instance = dev_instance;
+ if (hash_get (vxm->instance_used, user_instance))
+ {
+ pool_put (vxm->tunnels, t);
+ return VNET_API_ERROR_INSTANCE_IN_USE;
+ }
+ hash_set (vxm->instance_used, user_instance, 1);
+
+ t->dev_instance = dev_instance; /* actual */
+ t->user_instance = user_instance; /* name */
+
+ /* copy the key */
+ int add_failed;
+ if (is_ip6)
+ {
+ key6.value = (u64) dev_instance;
+ add_failed =
+ clib_bihash_add_del_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, &key6,
+ 1 /*add */ );
+ }
+ else
+ {
+ key4.value = (u64) dev_instance;
+ add_failed =
+ clib_bihash_add_del_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, &key4,
+ 1 /*add */ );
+ }
+
+ if (add_failed)
+ {
+ pool_put (vxm->tunnels, t);
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
+
+ vxlan_gbp_register_udp_ports ();
+
+ t->hw_if_index = vnet_register_interface
+ (vnm, vxlan_gbp_device_class.index, dev_instance,
+ vxlan_gbp_hw_class.index, dev_instance);
+ vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, t->hw_if_index);
+
+ /* Set vxlan_gbp tunnel output node */
+ u32 encap_index = !is_ip6 ?
+ vxlan4_gbp_encap_node.index : vxlan6_gbp_encap_node.index;
+ vnet_set_interface_output_node (vnm, t->hw_if_index, encap_index);
+
+ t->sw_if_index = sw_if_index = hi->sw_if_index;
+
+ if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode)
+ {
+ ip4_sw_interface_enable_disable (t->sw_if_index, 1);
+ ip6_sw_interface_enable_disable (t->sw_if_index, 1);
+ }
+
+ vec_validate_init_empty (vxm->tunnel_index_by_sw_if_index, sw_if_index,
+ ~0);
+ vxm->tunnel_index_by_sw_if_index[sw_if_index] = dev_instance;
+
+ /* setup l2 input config with l2 feature and bd 0 to drop packet */
+ vec_validate (l2im->configs, sw_if_index);
+ l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP;
+ l2im->configs[sw_if_index].bd_index = 0;
+
+ vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
+ si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN;
+ vnet_sw_interface_set_flags (vnm, sw_if_index,
+ VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+
+ fib_node_init (&t->node, FIB_NODE_TYPE_VXLAN_GBP_TUNNEL);
+ fib_prefix_t tun_dst_pfx;
+ vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL;
+
+ fib_prefix_from_ip46_addr (&t->dst, &tun_dst_pfx);
+ if (!ip46_address_is_multicast (&t->dst))
+ {
+ /* Unicast tunnel -
+ * source the FIB entry for the tunnel's destination
+ * and become a child thereof. The tunnel will then get poked
+ * when the forwarding for the entry updates, and the tunnel can
+ * re-stack accordingly
+ */
+ vtep_addr_ref (&t->src);
+ t->fib_entry_index = fib_entry_track (t->encap_fib_index,
+ &tun_dst_pfx,
+ FIB_NODE_TYPE_VXLAN_GBP_TUNNEL,
+ dev_instance,
+ &t->sibling_index);
+ vxlan_gbp_tunnel_restack_dpo (t);
+ }
+ else
+ {
+ /* Multicast tunnel -
+ * as the same mcast group can be used for multiple mcast tunnels
+ * with different VNIs, create the output fib adjacency only if
+ * it does not already exist
+ */
+ fib_protocol_t fp = fib_ip_proto (is_ip6);
+
+ if (vtep_addr_ref (&t->dst) == 1)
+ {
+ fib_node_index_t mfei;
+ adj_index_t ai;
+ fib_route_path_t path = {
+ .frp_proto = fib_proto_to_dpo (fp),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = 0xffffffff,
+ .frp_fib_index = ~0,
+ .frp_weight = 0,
+ .frp_flags = FIB_ROUTE_PATH_LOCAL,
+ .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
+ };
+ const mfib_prefix_t mpfx = {
+ .fp_proto = fp,
+ .fp_len = (is_ip6 ? 128 : 32),
+ .fp_grp_addr = tun_dst_pfx.fp_addr,
+ };
+
+ /*
+ * Setup the (*,G) to receive traffic on the mcast group
+ * - the forwarding interface is for-us
+ * - the accepting interface is that from the API
+ */
+ mfib_table_entry_path_update (t->encap_fib_index, &mpfx,
+ MFIB_SOURCE_VXLAN_GBP,
+ MFIB_ENTRY_FLAG_NONE, &path);
+
+ path.frp_sw_if_index = a->mcast_sw_if_index;
+ path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE;
+ path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT;
+ mfei = mfib_table_entry_path_update (
+ t->encap_fib_index, &mpfx, MFIB_SOURCE_VXLAN_GBP,
+ MFIB_ENTRY_FLAG_NONE, &path);
+
+ /*
+ * Create the mcast adjacency to send traffic to the group
+ */
+ ai = adj_mcast_add_or_lock (fp,
+ fib_proto_to_link (fp),
+ a->mcast_sw_if_index);
+
+ /*
+ * create a new end-point
+ */
+ mcast_shared_add (&t->dst, mfei, ai);
+ }
+
+ dpo_id_t dpo = DPO_INVALID;
+ mcast_shared_t ep = mcast_shared_get (&t->dst);
+
+ /* Stack shared mcast dst mac addr rewrite on encap */
+ dpo_set (&dpo, DPO_ADJACENCY_MCAST,
+ fib_proto_to_dpo (fp), ep.mcast_adj_index);
+
+ dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
+ dpo_reset (&dpo);
+ flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER;
+ }
+
+ vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class =
+ flood_class;
+ }
+ else
+ {
+ /* deleting a tunnel: tunnel must exist */
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ u32 instance = p[0];
+ t = pool_elt_at_index (vxm->tunnels, instance);
+
+ sw_if_index = t->sw_if_index;
+ vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
+
+ if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode)
+ {
+ ip4_sw_interface_enable_disable (t->sw_if_index, 0);
+ ip6_sw_interface_enable_disable (t->sw_if_index, 0);
+ }
+
+ vxm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
+
+ if (!is_ip6)
+ clib_bihash_add_del_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, &key4,
+ 0 /*del */ );
+ else
+ clib_bihash_add_del_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, &key6,
+ 0 /*del */ );
+
+ if (!ip46_address_is_multicast (&t->dst))
+ {
+ vtep_addr_unref (&t->src);
+ fib_entry_untrack (t->fib_entry_index, t->sibling_index);
+ }
+ else if (vtep_addr_unref (&t->dst) == 0)
+ {
+ mcast_shared_remove (&t->dst);
+ }
+
+ vxlan_gbp_unregister_udp_ports ();
+ vnet_delete_hw_interface (vnm, t->hw_if_index);
+ hash_unset (vxm->instance_used, t->user_instance);
+
+ fib_node_deinit (&t->node);
+ pool_put (vxm->tunnels, t);
+ }
+
+ if (sw_if_indexp)
+ *sw_if_indexp = sw_if_index;
+
+ return 0;
+}
+
+int
+vnet_vxlan_gbp_tunnel_del (u32 sw_if_index)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vxlan_gbp_tunnel_t *t = 0;
+ u32 ti;
+
+ if (sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index))
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ ti = vxm->tunnel_index_by_sw_if_index[sw_if_index];
+ if (~0 != ti)
+ {
+ t = pool_elt_at_index (vxm->tunnels, ti);
+
+ vnet_vxlan_gbp_tunnel_add_del_args_t args = {
+ .is_add = 0,
+ .is_ip6 = !ip46_address_is_ip4 (&t->src),
+ .vni = t->vni,
+ .src = t->src,
+ .dst = t->dst,
+ .instance = ~0,
+ };
+
+ return (vnet_vxlan_gbp_tunnel_add_del (&args, NULL));
+ }
+
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
+static uword
+get_decap_next_for_node (u32 node_index, u32 ipv4_set)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vlib_main_t *vm = vxm->vlib_main;
+ uword input_node = (ipv4_set) ? vxlan4_gbp_input_node.index :
+ vxlan6_gbp_input_node.index;
+
+ return vlib_node_add_next (vm, input_node, node_index);
+}
+
+static uword
+unformat_decap_next (unformat_input_t * input, va_list * args)
+{
+ u32 *result = va_arg (*args, u32 *);
+ u32 ipv4_set = va_arg (*args, int);
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vlib_main_t *vm = vxm->vlib_main;
+ u32 node_index;
+ u32 tmp;
+
+ if (unformat (input, "l2"))
+ *result = VXLAN_GBP_INPUT_NEXT_L2_INPUT;
+ else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index))
+ *result = get_decap_next_for_node (node_index, ipv4_set);
+ else if (unformat (input, "%d", &tmp))
+ *result = tmp;
+ else
+ return 0;
+ return 1;
+}
+
+static clib_error_t *
+vxlan_gbp_tunnel_add_del_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip46_address_t src = ip46_address_initializer, dst =
+ ip46_address_initializer;
+ vxlan_gbp_tunnel_mode_t mode = VXLAN_GBP_TUNNEL_MODE_L2;
+ u8 is_add = 1;
+ u8 src_set = 0;
+ u8 dst_set = 0;
+ u8 grp_set = 0;
+ u8 ipv4_set = 0;
+ u8 ipv6_set = 0;
+ u32 instance = ~0;
+ u32 encap_fib_index = 0;
+ u32 mcast_sw_if_index = ~0;
+ u32 decap_next_index = VXLAN_GBP_INPUT_NEXT_L2_INPUT;
+ u32 vni = 0;
+ u32 table_id;
+ clib_error_t *parse_error = NULL;
+
+ /* 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, "del"))
+ {
+ is_add = 0;
+ }
+ else if (unformat (line_input, "instance %d", &instance))
+ ;
+ else if (unformat (line_input, "src %U",
+ unformat_ip46_address, &src, IP46_TYPE_ANY))
+ {
+ src_set = 1;
+ ip46_address_is_ip4 (&src) ? (ipv4_set = 1) : (ipv6_set = 1);
+ }
+ else if (unformat (line_input, "dst %U",
+ unformat_ip46_address, &dst, IP46_TYPE_ANY))
+ {
+ dst_set = 1;
+ ip46_address_is_ip4 (&dst) ? (ipv4_set = 1) : (ipv6_set = 1);
+ }
+ else if (unformat (line_input, "group %U %U",
+ unformat_ip46_address, &dst, IP46_TYPE_ANY,
+ unformat_vnet_sw_interface,
+ vnet_get_main (), &mcast_sw_if_index))
+ {
+ grp_set = dst_set = 1;
+ ip46_address_is_ip4 (&dst) ? (ipv4_set = 1) : (ipv6_set = 1);
+ }
+ else if (unformat (line_input, "encap-vrf-id %d", &table_id))
+ {
+ encap_fib_index =
+ fib_table_find (fib_ip_proto (ipv6_set), table_id);
+ }
+ else if (unformat (line_input, "decap-next %U", unformat_decap_next,
+ &decap_next_index, ipv4_set))
+ ;
+ else if (unformat (line_input, "vni %d", &vni))
+ ;
+ else
+ {
+ parse_error = clib_error_return (0, "parse error: '%U'",
+ format_unformat_error, line_input);
+ break;
+ }
+ }
+
+ unformat_free (line_input);
+
+ if (parse_error)
+ return parse_error;
+
+ if (encap_fib_index == ~0)
+ return clib_error_return (0, "nonexistent encap-vrf-id %d", table_id);
+
+ if (src_set == 0)
+ return clib_error_return (0, "tunnel src address not specified");
+
+ if (dst_set == 0)
+ return clib_error_return (0, "tunnel dst address not specified");
+
+ if (grp_set && !ip46_address_is_multicast (&dst))
+ return clib_error_return (0, "tunnel group address not multicast");
+
+ if (grp_set == 0 && ip46_address_is_multicast (&dst))
+ return clib_error_return (0, "dst address must be unicast");
+
+ if (grp_set && mcast_sw_if_index == ~0)
+ return clib_error_return (0, "tunnel nonexistent multicast device");
+
+ if (ipv4_set && ipv6_set)
+ return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
+
+ if (ip46_address_cmp (&src, &dst) == 0)
+ return clib_error_return (0, "src and dst addresses are identical");
+
+ if (decap_next_index == ~0)
+ return clib_error_return (0, "next node not found");
+
+ if (vni == 0)
+ return clib_error_return (0, "vni not specified");
+
+ if (vni >> 24)
+ return clib_error_return (0, "vni %d out of range", vni);
+
+ vnet_vxlan_gbp_tunnel_add_del_args_t a = {
+ .is_add = is_add,
+ .is_ip6 = ipv6_set,
+ .instance = instance,
+#define _(x) .x = x,
+ foreach_copy_field
+#undef _
+ };
+
+ u32 tunnel_sw_if_index;
+ int rv = vnet_vxlan_gbp_tunnel_add_del (&a, &tunnel_sw_if_index);
+
+ switch (rv)
+ {
+ case 0:
+ if (is_add)
+ vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
+ vnet_get_main (), tunnel_sw_if_index);
+ break;
+
+ case VNET_API_ERROR_TUNNEL_EXIST:
+ return clib_error_return (0, "tunnel already exists...");
+
+ case VNET_API_ERROR_NO_SUCH_ENTRY:
+ return clib_error_return (0, "tunnel does not exist...");
+
+ case VNET_API_ERROR_INSTANCE_IN_USE:
+ return clib_error_return (0, "Instance is in use");
+
+ default:
+ return clib_error_return
+ (0, "vnet_vxlan_gbp_tunnel_add_del returned %d", rv);
+ }
+
+ return 0;
+}
+
+/*?
+ * Add or delete a VXLAN Tunnel.
+ *
+ * VXLAN provides the features needed to allow L2 bridge domains (BDs)
+ * to span multiple servers. This is done by building an L2 overlay on
+ * top of an L3 network underlay using VXLAN tunnels.
+ *
+ * This makes it possible for servers to be co-located in the same data
+ * center or be separated geographically as long as they are reachable
+ * through the underlay L3 network.
+ *
+ * You can refer to this kind of L2 overlay bridge domain as a VXLAN
+ * (Virtual eXtensible VLAN) segment.
+ *
+ * @cliexpar
+ * Example of how to create a VXLAN Tunnel:
+ * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 encap-vrf-id 7}
+ * Example of how to create a VXLAN Tunnel with a known name, vxlan_gbp_tunnel42:
+ * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 dst 10.0.3.3 instance 42}
+ * Example of how to create a multicast VXLAN Tunnel with a known name, vxlan_gbp_tunnel23:
+ * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 group 239.1.1.1 GigabitEthernet0/8/0 instance 23}
+ * Example of how to delete a VXLAN Tunnel:
+ * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 del}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (create_vxlan_gbp_tunnel_command, static) = {
+ .path = "create vxlan-gbp tunnel",
+ .short_help =
+ "create vxlan-gbp tunnel src <local-vtep-addr>"
+ " {dst <remote-vtep-addr>|group <mcast-vtep-addr> <intf-name>} vni <nn>"
+ " [instance <id>]"
+ " [encap-vrf-id <nn>] [decap-next [l2|node <name>]] [del]",
+ .function = vxlan_gbp_tunnel_add_del_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+show_vxlan_gbp_tunnel_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vxlan_gbp_tunnel_t *t;
+ int raw = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "raw"))
+ raw = 1;
+ else
+ return clib_error_return (0, "parse error: '%U'",
+ format_unformat_error, input);
+ }
+
+ if (pool_elts (vxm->tunnels) == 0)
+ vlib_cli_output (vm, "No vxlan-gbp tunnels configured...");
+
+/* *INDENT-OFF* */
+ pool_foreach (t, vxm->tunnels)
+ {
+ vlib_cli_output (vm, "%U", format_vxlan_gbp_tunnel, t);
+ }
+/* *INDENT-ON* */
+
+ if (raw)
+ {
+ vlib_cli_output (vm, "Raw IPv4 Hash Table:\n%U\n",
+ format_bihash_16_8, &vxm->vxlan4_gbp_tunnel_by_key,
+ 1 /* verbose */ );
+ vlib_cli_output (vm, "Raw IPv6 Hash Table:\n%U\n",
+ format_bihash_24_8, &vxm->vxlan6_gbp_tunnel_by_key,
+ 1 /* verbose */ );
+ }
+
+ return 0;
+}
+
+/*?
+ * Display all the VXLAN Tunnel entries.
+ *
+ * @cliexpar
+ * Example of how to display the VXLAN Tunnel entries:
+ * @cliexstart{show vxlan_gbp tunnel}
+ * [0] src 10.0.3.1 dst 10.0.3.3 vni 13 encap_fib_index 0 sw_if_index 5 decap_next l2
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_vxlan_gbp_tunnel_command, static) = {
+ .path = "show vxlan-gbp tunnel",
+ .short_help = "show vxlan-gbp tunnel [raw]",
+ .function = show_vxlan_gbp_tunnel_command_fn,
+};
+/* *INDENT-ON* */
+
+
+void
+vnet_int_vxlan_gbp_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
+{
+ if (is_ip6)
+ vnet_feature_enable_disable ("ip6-unicast", "ip6-vxlan-gbp-bypass",
+ sw_if_index, is_enable, 0, 0);
+ else
+ vnet_feature_enable_disable ("ip4-unicast", "ip4-vxlan-gbp-bypass",
+ sw_if_index, is_enable, 0, 0);
+}
+
+
+static clib_error_t *
+set_ip_vxlan_gbp_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_vxlan_gbp_bypass_mode (sw_if_index, is_ip6, is_enable);
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
+set_ip4_vxlan_gbp_bypass (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return set_ip_vxlan_gbp_bypass (0, input, cmd);
+}
+
+/*?
+ * This command adds the 'ip4-vxlan-gbp-bypass' graph node for a given
+ * interface. By adding the IPv4 vxlan_gbp-bypass graph node to an interface,
+ * the node checks for and validate input vxlan_gbp packet and bypass
+ * ip4-lookup, ip4-local, ip4-udp-lookup nodes to speedup vxlan_gbp packet
+ * forwarding. This node will cause extra overhead to for non-vxlan_gbp packets
+ * which is kept at a minimum.
+ *
+ * @cliexpar
+ * @parblock
+ * Example of graph node before ip4-vxlan_gbp-bypass is enabled:
+ * @cliexstart{show vlib graph ip4-vxlan_gbp-bypass}
+ * Name Next Previous
+ * ip4-vxlan-gbp-bypass error-drop [0]
+ * vxlan4-gbp-input [1]
+ * ip4-lookup [2]
+ * @cliexend
+ *
+ * Example of how to enable ip4-vxlan-gbp-bypass on an interface:
+ * @cliexcmd{set interface ip vxlan-gbp-bypass GigabitEthernet2/0/0}
+ *
+ * Example of graph node after ip4-vxlan-gbp-bypass is enabled:
+ * @cliexstart{show vlib graph ip4-vxlan-gbp-bypass}
+ * Name Next Previous
+ * ip4-vxlan-gbp-bypass error-drop [0] ip4-input
+ * vxlan4-gbp-input [1] ip4-input-no-checksum
+ * ip4-lookup [2]
+ * @cliexend
+ *
+ * Example of how to display the feature enabled on an interface:
+ * @cliexstart{show ip interface features GigabitEthernet2/0/0}
+ * IP feature paths configured on GigabitEthernet2/0/0...
+ * ...
+ * ipv4 unicast:
+ * ip4-vxlan-gbp-bypass
+ * ip4-lookup
+ * ...
+ * @cliexend
+ *
+ * Example of how to disable ip4-vxlan-gbp-bypass on an interface:
+ * @cliexcmd{set interface ip vxlan-gbp-bypass GigabitEthernet2/0/0 del}
+ * @endparblock
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_interface_ip_vxlan_gbp_bypass_command, static) = {
+ .path = "set interface ip vxlan-gbp-bypass",
+ .function = set_ip4_vxlan_gbp_bypass,
+ .short_help = "set interface ip vxlan-gbp-bypass <interface> [del]",
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+set_ip6_vxlan_gbp_bypass (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return set_ip_vxlan_gbp_bypass (1, input, cmd);
+}
+
+/*?
+ * This command adds the 'ip6-vxlan-gbp-bypass' graph node for a given
+ * interface. By adding the IPv6 vxlan-gbp-bypass graph node to an interface,
+ * the node checks for and validate input vxlan_gbp packet and bypass
+ * ip6-lookup, ip6-local, ip6-udp-lookup nodes to speedup vxlan_gbp packet
+ * forwarding. This node will cause extra overhead to for non-vxlan packets
+ * which is kept at a minimum.
+ *
+ * @cliexpar
+ * @parblock
+ * Example of graph node before ip6-vxlan-gbp-bypass is enabled:
+ * @cliexstart{show vlib graph ip6-vxlan-gbp-bypass}
+ * Name Next Previous
+ * ip6-vxlan-gbp-bypass error-drop [0]
+ * vxlan6-gbp-input [1]
+ * ip6-lookup [2]
+ * @cliexend
+ *
+ * Example of how to enable ip6-vxlan-gbp-bypass on an interface:
+ * @cliexcmd{set interface ip6 vxlan-gbp-bypass GigabitEthernet2/0/0}
+ *
+ * Example of graph node after ip6-vxlan-gbp-bypass is enabled:
+ * @cliexstart{show vlib graph ip6-vxlan-gbp-bypass}
+ * Name Next Previous
+ * ip6-vxlan-gbp-bypass error-drop [0] ip6-input
+ * vxlan6-gbp-input [1] ip4-input-no-checksum
+ * ip6-lookup [2]
+ * @cliexend
+ *
+ * Example of how to display the feature enabled on an interface:
+ * @cliexstart{show ip interface features GigabitEthernet2/0/0}
+ * IP feature paths configured on GigabitEthernet2/0/0...
+ * ...
+ * ipv6 unicast:
+ * ip6-vxlan-gbp-bypass
+ * ip6-lookup
+ * ...
+ * @cliexend
+ *
+ * Example of how to disable ip6-vxlan-gbp-bypass on an interface:
+ * @cliexcmd{set interface ip6 vxlan-gbp-bypass GigabitEthernet2/0/0 del}
+ * @endparblock
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_interface_ip6_vxlan_gbp_bypass_command, static) = {
+ .path = "set interface ip6 vxlan-gbp-bypass",
+ .function = set_ip6_vxlan_gbp_bypass,
+ .short_help = "set interface ip6 vxlan-gbp-bypass <interface> [del]",
+};
+/* *INDENT-ON* */
+
+#define VXLAN_GBP_HASH_NUM_BUCKETS (2 * 1024)
+#define VXLAN_GBP_HASH_MEMORY_SIZE (1 << 20)
+
+clib_error_t *
+vxlan_gbp_init (vlib_main_t * vm)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+
+ vxm->vnet_main = vnet_get_main ();
+ vxm->vlib_main = vm;
+
+ /* initialize the ip6 hash */
+ clib_bihash_init_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, "vxlan4-gbp",
+ VXLAN_GBP_HASH_NUM_BUCKETS,
+ VXLAN_GBP_HASH_MEMORY_SIZE);
+ clib_bihash_init_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, "vxlan6-gbp",
+ VXLAN_GBP_HASH_NUM_BUCKETS,
+ VXLAN_GBP_HASH_MEMORY_SIZE);
+ vxm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
+ vxm->mcast_shared = hash_create_mem (0,
+ sizeof (ip46_address_t),
+ sizeof (mcast_shared_t));
+
+ fib_node_register_type (FIB_NODE_TYPE_VXLAN_GBP_TUNNEL, &vxlan_gbp_vft);
+
+ punt_hdl = vlib_punt_client_register ("vxlan-gbp");
+
+ vlib_punt_reason_alloc (punt_hdl, "VXLAN-GBP-no-such-v4-tunnel", NULL, NULL,
+ &vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4],
+ VNET_PUNT_REASON_F_IP4_PACKET,
+ format_vnet_punt_reason_flags);
+ vlib_punt_reason_alloc (punt_hdl, "VXLAN-GBP-no-such-v6-tunnel", NULL, NULL,
+ &vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6],
+ VNET_PUNT_REASON_F_IP6_PACKET,
+ format_vnet_punt_reason_flags);
+
+ return (0);
+}
+
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (vxlan_gbp_init) =
+{
+ .runs_after = VLIB_INITS("punt_init"),
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h
new file mode 100644
index 00000000000..fe93587cb00
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+#ifndef included_vnet_vxlan_gbp_h
+#define included_vnet_vxlan_gbp_h
+
+#include <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/bihash_16_8.h>
+#include <vppinfra/bihash_24_8.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
+#include <vnet/l2/l2_bd.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/udp/udp_local.h>
+#include <vnet/udp/udp_packet.h>
+#include <vnet/dpo/dpo.h>
+#include <vnet/adj/adj_types.h>
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED (struct {
+ ip4_header_t ip4; /* 20 bytes */
+ udp_header_t udp; /* 8 bytes */
+ vxlan_gbp_header_t vxlan_gbp; /* 8 bytes */
+}) ip4_vxlan_gbp_header_t;
+
+typedef CLIB_PACKED (struct {
+ ip6_header_t ip6; /* 40 bytes */
+ udp_header_t udp; /* 8 bytes */
+ vxlan_gbp_header_t vxlan_gbp; /* 8 bytes */
+}) ip6_vxlan_gbp_header_t;
+/* *INDENT-ON* */
+
+/*
+* Key fields: remote ip, vni on incoming VXLAN packet
+* all fields in NET byte order
+*/
+typedef clib_bihash_kv_16_8_t vxlan4_gbp_tunnel_key_t;
+
+/*
+* Key fields: remote ip, vni and fib index on incoming VXLAN packet
+* ip, vni fields in NET byte order
+* fib index field in host byte order
+*/
+typedef clib_bihash_kv_24_8_t vxlan6_gbp_tunnel_key_t;
+
+typedef enum vxlan_gbp_tunnel_mode_t_
+{
+ VXLAN_GBP_TUNNEL_MODE_L2,
+ VXLAN_GBP_TUNNEL_MODE_L3,
+} vxlan_gbp_tunnel_mode_t;
+
+extern u8 *format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args);
+
+typedef struct
+{
+ /* Required for pool_get_aligned */
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+ /* FIB DPO for IP forwarding of VXLAN encap packet */
+ dpo_id_t next_dpo;
+
+ /* flags */
+ u16 flags;
+
+ /* vxlan VNI in HOST byte order */
+ u32 vni;
+
+ /* tunnel src and dst addresses */
+ ip46_address_t src;
+ ip46_address_t dst;
+
+ /* mcast packet output intfc index (used only if dst is mcast) */
+ u32 mcast_sw_if_index;
+
+ /* The FIB index for src/dst addresses */
+ u32 encap_fib_index;
+
+ /* vnet intfc index */
+ u32 sw_if_index;
+ u32 hw_if_index;
+
+ /** Next node after VxLAN-GBP encap */
+ uword encap_next_node;
+
+ /**
+ * Tunnel mode.
+ * L2 tunnels decap to L2 path, L3 tunnels to the L3 path
+ */
+ vxlan_gbp_tunnel_mode_t mode;
+
+ /**
+ * Linkage into the FIB object graph
+ */
+ fib_node_t node;
+
+ /*
+ * The FIB entry for (depending on VXLAN-GBP tunnel is unicast or mcast)
+ * sending unicast VXLAN-GBP encap packets or receiving mcast VXLAN-GBP packets
+ */
+ fib_node_index_t fib_entry_index;
+ adj_index_t mcast_adj_index;
+
+ /**
+ * The tunnel is a child of the FIB entry for its destination. This is
+ * so it receives updates when the forwarding information for that entry
+ * changes.
+ * The tunnels sibling index on the FIB entry's dependency list.
+ */
+ u32 sibling_index;
+
+ u32 dev_instance; /* Real device instance in tunnel vector */
+ u32 user_instance; /* Instance name being shown to user */
+
+
+ VNET_DECLARE_REWRITE;
+} vxlan_gbp_tunnel_t;
+
+#define foreach_vxlan_gbp_input_next \
+ _(DROP, "error-drop") \
+ _(PUNT, "punt-dispatch") \
+ _(L2_INPUT, "l2-input") \
+ _(IP4_INPUT, "ip4-input") \
+ _(IP6_INPUT, "ip6-input")
+
+typedef enum
+{
+#define _(s,n) VXLAN_GBP_INPUT_NEXT_##s,
+ foreach_vxlan_gbp_input_next
+#undef _
+ VXLAN_GBP_INPUT_N_NEXT,
+} vxlan_gbp_input_next_t;
+
+typedef enum
+{
+#define vxlan_gbp_error(n,s) VXLAN_GBP_ERROR_##n,
+#include <vnet/vxlan-gbp/vxlan_gbp_error.def>
+#undef vxlan_gbp_error
+ VXLAN_GBP_N_ERROR,
+} vxlan_gbp_input_error_t;
+
+/**
+ * Call back function packets that do not match a configured tunnel
+ */
+typedef vxlan_gbp_input_next_t (*vxlan_bgp_no_tunnel_t) (vlib_buffer_t * b,
+ u32 thread_index,
+ u8 is_ip6);
+
+typedef struct
+{
+ /* vector of encap tunnel instances */
+ vxlan_gbp_tunnel_t *tunnels;
+
+ /* lookup tunnel by key */
+ clib_bihash_16_8_t vxlan4_gbp_tunnel_by_key; /* keyed on ipv4.dst + fib + vni */
+ clib_bihash_24_8_t vxlan6_gbp_tunnel_by_key; /* keyed on ipv6.dst + fib + vni */
+
+ /* local VTEP IPs ref count used by vxlan-bypass node to check if
+ received VXLAN packet DIP matches any local VTEP address */
+ uword *vtep4; /* local ip4 VTEPs keyed on their ip4 addr */
+ uword *vtep6; /* local ip6 VTEPs keyed on their ip6 addr */
+
+ /* mcast shared info */
+ uword *mcast_shared; /* keyed on mcast ip46 addr */
+
+ /* Mapping from sw_if_index to tunnel index */
+ u32 *tunnel_index_by_sw_if_index;
+
+ /* On demand udp port registration */
+ u32 udp_ports_registered;
+
+ /* convenience */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+
+ /* Record used instances */
+ uword *instance_used;
+
+ /**
+ * Punt reasons for no such tunnel
+ */
+ vlib_punt_reason_t punt_no_such_tunnel[FIB_PROTOCOL_IP_MAX];
+} vxlan_gbp_main_t;
+
+extern vxlan_gbp_main_t vxlan_gbp_main;
+
+extern vlib_node_registration_t vxlan4_gbp_input_node;
+extern vlib_node_registration_t vxlan6_gbp_input_node;
+extern vlib_node_registration_t vxlan4_gbp_encap_node;
+extern vlib_node_registration_t vxlan6_gbp_encap_node;
+extern void vxlan_gbp_register_udp_ports (void);
+extern void vxlan_gbp_unregister_udp_ports (void);
+
+u8 *format_vxlan_gbp_encap_trace (u8 * s, va_list * args);
+
+typedef struct
+{
+ u8 is_add;
+ u8 is_ip6;
+ u32 instance;
+ vxlan_gbp_tunnel_mode_t mode;
+ ip46_address_t src, dst;
+ u32 mcast_sw_if_index;
+ u32 encap_fib_index;
+ u32 vni;
+} vnet_vxlan_gbp_tunnel_add_del_args_t;
+
+int vnet_vxlan_gbp_tunnel_add_del
+ (vnet_vxlan_gbp_tunnel_add_del_args_t * a, u32 * sw_if_indexp);
+int vnet_vxlan_gbp_tunnel_del (u32 sw_if_indexp);
+
+void vnet_int_vxlan_gbp_bypass_mode (u32 sw_if_index, u8 is_ip6,
+ u8 is_enable);
+
+always_inline u32
+vxlan_gbp_tunnel_by_sw_if_index (u32 sw_if_index)
+{
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+
+ if (sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index))
+ return ~0;
+
+ return (vxm->tunnel_index_by_sw_if_index[sw_if_index]);
+}
+
+#endif /* included_vnet_vxlan_gbp_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c
new file mode 100644
index 00000000000..a3f2246f463
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c
@@ -0,0 +1,217 @@
+/*
+ *------------------------------------------------------------------
+ * vxlan_gbp_api.c - vxlan gbp api
+ *
+ * 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 <vnet/vnet.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/interface.h>
+#include <vnet/api_errno.h>
+#include <vnet/feature/feature.h>
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/format_fns.h>
+
+#include <vxlan-gbp/vxlan_gbp.api_enum.h>
+#include <vxlan-gbp/vxlan_gbp.api_types.h>
+
+#define REPLY_MSG_ID_BASE msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+static u16 msg_id_base;
+
+static void
+ vl_api_sw_interface_set_vxlan_gbp_bypass_t_handler
+ (vl_api_sw_interface_set_vxlan_gbp_bypass_t * mp)
+{
+ vl_api_sw_interface_set_vxlan_gbp_bypass_reply_t *rmp;
+ int rv = 0;
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ vnet_int_vxlan_gbp_bypass_mode (sw_if_index, mp->is_ipv6, mp->enable);
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_SW_INTERFACE_SET_VXLAN_GBP_BYPASS_REPLY);
+}
+
+static int
+vxlan_gbp_tunnel_mode_decode (vl_api_vxlan_gbp_api_tunnel_mode_t in,
+ vxlan_gbp_tunnel_mode_t * out)
+{
+ in = clib_net_to_host_u32 (in);
+
+ switch (in)
+ {
+ case VXLAN_GBP_API_TUNNEL_MODE_L2:
+ *out = VXLAN_GBP_TUNNEL_MODE_L2;
+ return (0);
+ case VXLAN_GBP_API_TUNNEL_MODE_L3:
+ *out = VXLAN_GBP_TUNNEL_MODE_L3;
+ return (0);
+ }
+ return (VNET_API_ERROR_INVALID_VALUE);
+}
+
+static void vl_api_vxlan_gbp_tunnel_add_del_t_handler
+ (vl_api_vxlan_gbp_tunnel_add_del_t * mp)
+{
+ vl_api_vxlan_gbp_tunnel_add_del_reply_t *rmp;
+ vxlan_gbp_tunnel_mode_t mode;
+ ip46_address_t src, dst;
+ ip46_type_t itype;
+ int rv = 0;
+ u32 sw_if_index = ~0;
+ u32 fib_index;
+
+ itype = ip_address_decode (&mp->tunnel.src, &src);
+ itype = ip_address_decode (&mp->tunnel.dst, &dst);
+
+ fib_index = fib_table_find (fib_proto_from_ip46 (itype),
+ ntohl (mp->tunnel.encap_table_id));
+ if (fib_index == ~0)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_FIB;
+ goto out;
+ }
+
+ rv = vxlan_gbp_tunnel_mode_decode (mp->tunnel.mode, &mode);
+
+ if (rv)
+ goto out;
+
+ vnet_vxlan_gbp_tunnel_add_del_args_t a = {
+ .is_add = mp->is_add,
+ .is_ip6 = (itype == IP46_TYPE_IP6),
+ .instance = ntohl (mp->tunnel.instance),
+ .mcast_sw_if_index = ntohl (mp->tunnel.mcast_sw_if_index),
+ .encap_fib_index = fib_index,
+ .vni = ntohl (mp->tunnel.vni),
+ .dst = dst,
+ .src = src,
+ .mode = mode,
+ };
+
+ /* Check src & dst are different */
+ if (ip46_address_cmp (&a.dst, &a.src) == 0)
+ {
+ rv = VNET_API_ERROR_SAME_SRC_DST;
+ goto out;
+ }
+ if (ip46_address_is_multicast (&a.dst) &&
+ !vnet_sw_if_index_is_api_valid (a.mcast_sw_if_index))
+ {
+ rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ goto out;
+ }
+
+ rv = vnet_vxlan_gbp_tunnel_add_del (&a, &sw_if_index);
+
+out:
+ /* *INDENT-OFF* */
+ REPLY_MACRO2(VL_API_VXLAN_GBP_TUNNEL_ADD_DEL_REPLY,
+ ({
+ rmp->sw_if_index = ntohl (sw_if_index);
+ }));
+ /* *INDENT-ON* */
+}
+
+static void send_vxlan_gbp_tunnel_details
+ (vxlan_gbp_tunnel_t * t, vl_api_registration_t * reg, u32 context)
+{
+ vl_api_vxlan_gbp_tunnel_details_t *rmp;
+ ip46_type_t itype = (ip46_address_is_ip4 (&t->dst) ?
+ IP46_TYPE_IP4 : IP46_TYPE_IP6);
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ clib_memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id =
+ ntohs (VL_API_VXLAN_GBP_TUNNEL_DETAILS + REPLY_MSG_ID_BASE);
+
+ ip_address_encode (&t->src, itype, &rmp->tunnel.src);
+ ip_address_encode (&t->dst, itype, &rmp->tunnel.dst);
+ rmp->tunnel.encap_table_id =
+ fib_table_get_table_id (t->encap_fib_index, fib_proto_from_ip46 (itype));
+
+ rmp->tunnel.instance = htonl (t->user_instance);
+ rmp->tunnel.mcast_sw_if_index = htonl (t->mcast_sw_if_index);
+ rmp->tunnel.vni = htonl (t->vni);
+ rmp->tunnel.sw_if_index = htonl (t->sw_if_index);
+ rmp->context = context;
+
+ vl_api_send_msg (reg, (u8 *) rmp);
+}
+
+static void vl_api_vxlan_gbp_tunnel_dump_t_handler
+ (vl_api_vxlan_gbp_tunnel_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+ vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+ vxlan_gbp_tunnel_t *t;
+ u32 sw_if_index;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ sw_if_index = ntohl (mp->sw_if_index);
+
+ if (~0 == sw_if_index)
+ {
+ /* *INDENT-OFF* */
+ pool_foreach (t, vxm->tunnels)
+ {
+ send_vxlan_gbp_tunnel_details(t, reg, mp->context);
+ }
+ /* *INDENT-ON* */
+ }
+ else
+ {
+ if ((sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index)) ||
+ (~0 == vxm->tunnel_index_by_sw_if_index[sw_if_index]))
+ {
+ return;
+ }
+ t = &vxm->tunnels[vxm->tunnel_index_by_sw_if_index[sw_if_index]];
+ send_vxlan_gbp_tunnel_details (t, reg, mp->context);
+ }
+}
+
+#include <vxlan-gbp/vxlan_gbp.api.c>
+static clib_error_t *
+vxlan_gbp_api_hookup (vlib_main_t * vm)
+{
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ msg_id_base = setup_message_id_table ();
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (vxlan_gbp_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/libmemif/test/main_test.h b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def
index 300d8a8e46b..43ad4dac064 100644
--- a/extras/libmemif/test/main_test.h
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def
@@ -1,6 +1,5 @@
/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 Cisco and/or its affiliates.
+ * 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:
@@ -12,14 +11,7 @@
* 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 _MAIN_TEST_H_
-#define _MAIN_TEST_H_
-
-#include <unit_test.h>
-
-Suite *main_suite ();
-
-#endif /* _MAIN_TEST_H_ */
+vxlan_gbp_error (DECAPSULATED, "good packets decapsulated")
+vxlan_gbp_error (NO_SUCH_TUNNEL, "no such tunnel packets")
+vxlan_gbp_error (BAD_FLAGS, "packets with bad flags field in vxlan gbp header")
diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c
new file mode 100644
index 00000000000..01c7a19bfb9
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c
@@ -0,0 +1,60 @@
+/*
+ * 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 <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+
+u8 *
+format_vxlan_gbp_header_flags (u8 * s, va_list * args)
+{
+ vxlan_gbp_flags_t flags = va_arg (*args, int);
+
+ if (VXLAN_GBP_FLAGS_NONE == flags)
+ {
+ s = format (s, "None");
+ }
+#define _(n,f) { \
+ if (VXLAN_GBP_FLAGS_##f & flags) \
+ s = format (s, #f); \
+ }
+ foreach_vxlan_gbp_flags
+#undef _
+ return (s);
+}
+
+u8 *
+format_vxlan_gbp_header_gpflags (u8 * s, va_list * args)
+{
+ vxlan_gbp_gpflags_t flags = va_arg (*args, int);
+
+ if (VXLAN_GBP_GPFLAGS_NONE == flags)
+ {
+ s = format (s, "None");
+ }
+#define _(n,f) { \
+ if (VXLAN_GBP_GPFLAGS_##f & flags) \
+ s = format (s, #f); \
+ }
+ foreach_vxlan_gbp_gpflags
+#undef _
+ return (s);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h
new file mode 100644
index 00000000000..e655b333b89
--- /dev/null
+++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+#ifndef __included_vxlan_gbp_packet_h__
+#define __included_vxlan_gbp_packet_h__ 1
+
+#include <vlib/vlib.h>
+
+/*
+ * From draft-smith-vxlan-group-policy-04.txt
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |G|R|R|R|I|R|R|R|R|D|E|S|A|R|R|R| Group Policy ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VXLAN Network Identifier (VNI) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * G bit: Bit 0 of the initial word is defined as the G (Group Based
+ * Policy Extension) bit.
+ *
+ * I bit: where the I flag MUST be set to 1 for a valid
+ * VXLAN Network ID (VNI).
+ *
+ * D bit: Bit 9 of the initial word is defined as the Don't Learn bit.
+ * When set, this bit indicates that the egress VTEP MUST NOT learn the
+ * source address of the encapsulated frame.
+ *
+ * E bit: Bit 10 of the initial word is defined as the bounce packet.
+ * When set, this bit indicates that packet is bounced and must be
+ * dropped.
+ *
+ * S bit: Bit 11 of the initial word is defined as the source policy
+ * applied bit.
+ *
+ * A bit: Bit 12 of the initial word is defined as the A (Policy
+ * Applied) bit. This bit is only defined as the A bit when the G bit
+ * is set to 1.
+ *
+ * A = 1 indicates that the group policy has already been applied to
+ * this packet. Policies MUST NOT be applied by devices when the A
+ * bit is set.
+ *
+ * A = 0 indicates that the group policy has not been applied to this
+ * packet. Group policies MUST be applied by devices when the A bit
+ * is set to 0 and the destination Group has been determined.
+ * Devices that apply the Group policy MUST set the A bit to 1 after
+ * the policy has been applied.
+ *
+ * Group Policy ID: 16 bit identifier that indicates the source TSI
+ * Group membership being encapsulated by VXLAN. Its value is source
+ * class id.
+ *
+ * FOR INTERNAL USE ONLY
+ * R bit: Bit 12 of the initial word is defined as the reflection bit
+ * Set on packet rx checked on tx and dropped if set. this prevents
+ * packets recieved on an iVXLAN tunnel being reflected back to
+ * another.
+ */
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+ union
+ {
+ struct
+ {
+ u8 flag_g_i;
+ u8 gpflags;
+ };
+ u16 flags;
+ };
+ u16 sclass;
+ };
+ u32 flags_sclass_as_u32;
+ };
+ u32 vni_reserved;
+} vxlan_gbp_header_t;
+
+#define foreach_vxlan_gbp_flags \
+ _ (0x80, G) \
+ _ (0x08, I)
+
+typedef enum
+{
+ VXLAN_GBP_FLAGS_NONE = 0,
+#define _(n,f) VXLAN_GBP_FLAGS_##f = n,
+ foreach_vxlan_gbp_flags
+#undef _
+} __attribute__ ((packed)) vxlan_gbp_flags_t;
+
+#define VXLAN_GBP_FLAGS_GI (VXLAN_GBP_FLAGS_G|VXLAN_GBP_FLAGS_I)
+
+#define foreach_vxlan_gbp_gpflags \
+_ (0x40, D) \
+_ (0x20, E) \
+_ (0x10, S) \
+_ (0x08, A) \
+_ (0x04, R)
+
+typedef enum
+{
+ VXLAN_GBP_GPFLAGS_NONE = 0,
+#define _(n,f) VXLAN_GBP_GPFLAGS_##f = n,
+ foreach_vxlan_gbp_gpflags
+#undef _
+} __attribute__ ((packed)) vxlan_gbp_gpflags_t;
+
+static inline u32
+vxlan_gbp_get_vni (vxlan_gbp_header_t * h)
+{
+ u32 vni_reserved_host_byte_order;
+
+ vni_reserved_host_byte_order = clib_net_to_host_u32 (h->vni_reserved);
+ return vni_reserved_host_byte_order >> 8;
+}
+
+static inline u16
+vxlan_gbp_get_sclass (vxlan_gbp_header_t * h)
+{
+ u16 sclass_host_byte_order;
+
+ sclass_host_byte_order = clib_net_to_host_u16 (h->sclass);
+ return sclass_host_byte_order;
+}
+
+static inline vxlan_gbp_gpflags_t
+vxlan_gbp_get_gpflags (vxlan_gbp_header_t * h)
+{
+ return h->gpflags;
+}
+
+static inline vxlan_gbp_flags_t
+vxlan_gbp_get_flags (vxlan_gbp_header_t * h)
+{
+ return h->flag_g_i;
+}
+
+static inline void
+vxlan_gbp_set_header (vxlan_gbp_header_t * h, u32 vni)
+{
+ h->vni_reserved = clib_host_to_net_u32 (vni << 8);
+ h->flags_sclass_as_u32 = 0;
+ h->flag_g_i = VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G;
+}
+
+extern u8 *format_vxlan_gbp_header_flags (u8 * s, va_list * args);
+extern u8 *format_vxlan_gbp_header_gpflags (u8 * s, va_list * args);
+
+#endif /* __included_vxlan_gbp_packet_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vom/test/test_vom.py b/extras/deprecated/vom/test/test_vom.py
index a77b935263d..51944296233 100644
--- a/extras/deprecated/vom/test/test_vom.py
+++ b/extras/deprecated/vom/test/test_vom.py
@@ -4,13 +4,12 @@
import unittest
import os
import signal
-from framework import VppTestCase, running_extended_tests, \
- VppTestRunner, Worker
+from framework import VppTestCase, running_extended_tests, VppTestRunner, Worker
@unittest.skipUnless(running_extended_tests, "part of extended tests")
class VOMTestCase(VppTestCase):
- """ VPP Object Model Test """
+ """VPP Object Model Test"""
@classmethod
def setUpClass(cls):
@@ -21,14 +20,14 @@ class VOMTestCase(VppTestCase):
super(VOMTestCase, cls).tearDownClass()
def test_vom_cpp(self):
- """ run C++ VOM tests """
+ """run C++ VOM tests"""
var = "TEST_BR"
built_root = os.getenv(var, None)
- self.assertIsNotNone(built_root,
- "Environment variable `%s' not set" % var)
+ self.assertIsNotNone(built_root, "Environment variable `%s' not set" % var)
executable = "%s/vom_test/vom_test" % built_root
- worker = Worker([executable, "vpp object model",
- self.get_api_segment_prefix()], self.logger)
+ worker = Worker(
+ [executable, "vpp object model", self.get_api_segment_prefix()], self.logger
+ )
worker.start()
timeout = 120
worker.join(timeout)
@@ -37,17 +36,15 @@ class VOMTestCase(VppTestCase):
if worker.result is None:
try:
error = True
- self.logger.error(
- "Timeout! Worker did not finish in %ss" % timeout)
+ self.logger.error("Timeout! Worker did not finish in %ss" % timeout)
os.killpg(os.getpgid(worker.process.pid), signal.SIGTERM)
worker.join()
except:
raise Exception("Couldn't kill worker-spawned process")
if error:
- raise Exception(
- "Timeout! Worker did not finish in %ss" % timeout)
+ raise Exception("Timeout! Worker did not finish in %ss" % timeout)
self.assert_equal(worker.result, 0, "Binary test return code")
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main(testRunner=VppTestRunner)
diff --git a/extras/deprecated/vom/vom/cmd.hpp b/extras/deprecated/vom/vom/cmd.hpp
index b95de41095e..e84c496ca75 100644
--- a/extras/deprecated/vom/vom/cmd.hpp
+++ b/extras/deprecated/vom/vom/cmd.hpp
@@ -53,7 +53,7 @@ public:
/**
* Invoked on a Command when the HW queue is disabled to indicate
- * that the commnad can be considered successful
+ * that the command can be considered successful
*/
virtual void succeeded() = 0;
diff --git a/extras/deprecated/vom/vom/ra_config.hpp b/extras/deprecated/vom/vom/ra_config.hpp
index f5b782a72d2..421d218007e 100644
--- a/extras/deprecated/vom/vom/ra_config.hpp
+++ b/extras/deprecated/vom/vom/ra_config.hpp
@@ -85,7 +85,7 @@ private:
/**
* Use the source address of the router-solicitation message if
- * availiable.
+ * available.
*/
uint8_t m_send_unicast;
diff --git a/extras/deprecated/vom/vom/ra_prefix.hpp b/extras/deprecated/vom/vom/ra_prefix.hpp
index 0be40c403b3..12c32c8409b 100644
--- a/extras/deprecated/vom/vom/ra_prefix.hpp
+++ b/extras/deprecated/vom/vom/ra_prefix.hpp
@@ -111,7 +111,7 @@ private:
uint32_t m_val_lifetime;
/**
- * '<pref-lifetime>' is the prefered-lifetime and is the length of
+ * '<pref-lifetime>' is the preferred-lifetime and is the length of
* time in seconds during what addresses generated from the prefix
* remain preferred.
*
diff --git a/extras/deprecated/vppinfra/graph.c b/extras/deprecated/vppinfra/graph.c
new file mode 100644
index 00000000000..4c92f8ef45f
--- /dev/null
+++ b/extras/deprecated/vppinfra/graph.c
@@ -0,0 +1,182 @@
+/*
+ * 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 <vppinfra/graph.h>
+
+/* Set link distance, creating link if not found. */
+u32
+graph_set_link (graph_t * g, u32 src, u32 dst, u32 distance)
+{
+ graph_node_t *src_node, *dst_node;
+ graph_link_t *l;
+ u32 old_distance;
+
+ /* The following validate will not work if src or dst are on the
+ pool free list. */
+ if (src < vec_len (g->nodes))
+ ASSERT (!pool_is_free_index (g->nodes, src));
+ if (dst < vec_len (g->nodes))
+ ASSERT (!pool_is_free_index (g->nodes, dst));
+
+ /* Make new (empty) nodes to make src and dst valid. */
+ pool_validate_index (g->nodes, clib_max (src, dst));
+
+ src_node = pool_elt_at_index (g->nodes, src);
+ dst_node = pool_elt_at_index (g->nodes, dst);
+
+ l = graph_dir_get_link_to_node (&src_node->next, dst);
+ if (l)
+ {
+ old_distance = l->distance;
+ l->distance = distance;
+
+ l = graph_dir_get_link_to_node (&dst_node->prev, src);
+ l->distance = distance;
+ }
+ else
+ {
+ uword li_next, li_prev;
+
+ old_distance = ~0;
+
+ li_next = graph_dir_add_link (&src_node->next, dst, distance);
+ li_prev = graph_dir_add_link (&dst_node->prev, src, distance);
+
+ l = vec_elt_at_index (src_node->next.links, li_next);
+ l->link_to_self_index = li_prev;
+
+ l = vec_elt_at_index (dst_node->prev.links, li_prev);
+ l->link_to_self_index = li_next;
+ }
+
+ return old_distance;
+}
+
+void
+graph_del_link (graph_t * g, u32 src, u32 dst)
+{
+ graph_node_t *src_node, *dst_node;
+
+ src_node = pool_elt_at_index (g->nodes, src);
+ dst_node = pool_elt_at_index (g->nodes, dst);
+
+ graph_dir_del_link (&src_node->next, dst);
+ graph_dir_del_link (&dst_node->next, src);
+}
+
+/* Delete source node and all links from other nodes from/to source. */
+uword
+graph_del_node (graph_t * g, u32 src)
+{
+ graph_node_t *src_node, *n;
+ uword index;
+ graph_link_t *l;
+
+ src_node = pool_elt_at_index (g->nodes, src);
+
+ vec_foreach (l, src_node->next.links)
+ {
+ n = pool_elt_at_index (g->nodes, l->node_index);
+ graph_dir_del_link (&n->prev, src);
+ }
+
+ vec_foreach (l, src_node->prev.links)
+ {
+ n = pool_elt_at_index (g->nodes, l->node_index);
+ graph_dir_del_link (&n->next, src);
+ }
+
+ graph_dir_free (&src_node->next);
+ graph_dir_free (&src_node->prev);
+
+ index = src_node - g->nodes;
+ pool_put (g->nodes, src_node);
+ clib_memset (src_node, ~0, sizeof (src_node[0]));
+
+ return index;
+}
+
+uword
+unformat_graph (unformat_input_t * input, va_list * args)
+{
+ graph_t *g = va_arg (*args, graph_t *);
+ typedef struct
+ {
+ u32 src, dst, distance;
+ } T;
+ T *links = 0, *l;
+ uword result;
+
+ while (1)
+ {
+ vec_add2 (links, l, 1);
+ if (!unformat (input, "%d%d%d", &l->src, &l->dst, &l->distance))
+ break;
+ }
+ _vec_len (links) -= 1;
+ result = vec_len (links) > 0;
+ vec_foreach (l, links)
+ {
+ graph_set_link (g, l->src, l->dst, l->distance);
+ graph_set_link (g, l->dst, l->src, l->distance);
+ }
+
+ vec_free (links);
+ return result;
+}
+
+u8 *
+format_graph_node (u8 * s, va_list * args)
+{
+ graph_t *g = va_arg (*args, graph_t *);
+ u32 node_index = va_arg (*args, u32);
+
+ if (g->format_node)
+ s = format (s, "%U", g->format_node, g, node_index);
+ else
+ s = format (s, "%d", node_index);
+
+ return s;
+}
+
+u8 *
+format_graph (u8 * s, va_list * args)
+{
+ graph_t *g = va_arg (*args, graph_t *);
+ graph_node_t *n;
+ graph_link_t *l;
+ u32 indent = format_get_indent (s);
+
+ s = format (s, "graph %d nodes", pool_elts (g->nodes));
+ /* *INDENT-OFF* */
+ pool_foreach (n, g->nodes) {
+ s = format (s, "\n%U", format_white_space, indent + 2);
+ s = format (s, "%U -> ", format_graph_node, g, n - g->nodes);
+ vec_foreach (l, n->next.links)
+ s = format (s, "%U (%d), ",
+ format_graph_node, g, l->node_index,
+ l->distance);
+ }
+ /* *INDENT-ON* */
+
+ return s;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vppinfra/graph.h b/extras/deprecated/vppinfra/graph.h
new file mode 100644
index 00000000000..1c26118f76c
--- /dev/null
+++ b/extras/deprecated/vppinfra/graph.h
@@ -0,0 +1,127 @@
+/*
+ * 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_clib_graph_h
+#define included_clib_graph_h
+
+#include <vppinfra/format.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/pool.h>
+
+/* Generic graphs. */
+typedef struct
+{
+ /* Next node along this link. */
+ u32 node_index;
+
+ /* Other direction link index to reach back to current node. */
+ u32 link_to_self_index;
+
+ /* Distance to next node. */
+ u32 distance;
+} graph_link_t;
+
+/* Direction on graph: either next or previous. */
+typedef struct
+{
+ /* Vector of links. */
+ graph_link_t *links;
+
+ /* Hash mapping node index to link which visits this node. */
+ uword *link_index_by_node_index;
+} graph_dir_t;
+
+always_inline void
+graph_dir_free (graph_dir_t * d)
+{
+ vec_free (d->links);
+ hash_free (d->link_index_by_node_index);
+}
+
+always_inline graph_link_t *
+graph_dir_get_link_to_node (graph_dir_t * d, u32 node_index)
+{
+ uword *p = hash_get (d->link_index_by_node_index, node_index);
+ return p ? vec_elt_at_index (d->links, p[0]) : 0;
+}
+
+always_inline uword
+graph_dir_add_link (graph_dir_t * d, u32 node_index, u32 distance)
+{
+ graph_link_t *l;
+ ASSERT (!graph_dir_get_link_to_node (d, node_index));
+ vec_add2 (d->links, l, 1);
+ l->node_index = node_index;
+ l->distance = distance;
+ hash_set (d->link_index_by_node_index, node_index, l - d->links);
+ return l - d->links;
+}
+
+always_inline void
+graph_dir_del_link (graph_dir_t * d, u32 node_index)
+{
+ graph_link_t *l = graph_dir_get_link_to_node (d, node_index);
+ uword li = l - d->links;
+ uword n_links = vec_len (d->links);
+
+ ASSERT (l != 0);
+ hash_unset (d->link_index_by_node_index, node_index);
+ n_links -= 1;
+ if (li < n_links)
+ d->links[li] = d->links[n_links];
+ _vec_len (d->links) = n_links;
+}
+
+typedef struct
+{
+ /* Nodes we are connected to plus distances. */
+ graph_dir_t next, prev;
+} graph_node_t;
+
+typedef struct
+{
+ /* Pool of nodes. */
+ graph_node_t *nodes;
+
+ void *opaque;
+
+ format_function_t *format_node;
+} graph_t;
+
+/* Set link distance, creating link if not found. */
+u32 graph_set_link (graph_t * g, u32 src, u32 dst, u32 distance);
+
+always_inline void
+graph_set_bidirectional_link (graph_t * g, u32 src, u32 dst, u32 distance)
+{
+ graph_set_link (g, src, dst, distance);
+ graph_set_link (g, dst, src, distance);
+}
+
+void graph_del_link (graph_t * g, u32 src, u32 dst);
+uword graph_del_node (graph_t * g, u32 src);
+
+unformat_function_t unformat_graph;
+format_function_t format_graph;
+format_function_t format_graph_node;
+
+#endif /* included_clib_graph_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/deprecated/vppinfra/test_mheap.c b/extras/deprecated/vppinfra/test_mheap.c
new file mode 100644
index 00000000000..ae0c58a6a74
--- /dev/null
+++ b/extras/deprecated/vppinfra/test_mheap.c
@@ -0,0 +1,286 @@
+/*
+ * 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) 2001, 2002, 2003 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.
+*/
+
+#ifdef CLIB_LINUX_KERNEL
+#include <linux/unistd.h>
+#endif
+
+#ifdef CLIB_UNIX
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h> /* scanf */
+#endif
+
+#include <vppinfra/format.h>
+#include <vppinfra/random.h>
+#include <vppinfra/time.h>
+
+static int verbose = 0;
+#define if_verbose(format,args...) \
+ if (verbose) { clib_warning(format, ## args); }
+
+int
+test1 (void)
+{
+ clib_time_t clib_time;
+ void *h_mem = clib_mem_alloc (2ULL << 30);
+ void *h;
+ uword *objects = 0;
+ int i;
+ f64 before, after;
+
+ clib_time_init (&clib_time);
+
+ vec_validate (objects, 2000000 - 1);
+
+ h = mheap_alloc (h_mem, (uword) (2 << 30));
+
+ before = clib_time_now (&clib_time);
+
+ for (i = 0; i < vec_len (objects); i++)
+ {
+ h = mheap_get_aligned (h, 24 /* size */ ,
+ 64 /* align */ ,
+ 16 /* align at offset */ , &objects[i]);
+ }
+
+ after = clib_time_now (&clib_time);
+
+ fformat (stdout, "alloc: %u objects in %.2f seconds, %.2f objects/second\n",
+ vec_len (objects), (after - before),
+ ((f64) vec_len (objects)) / (after - before));
+
+ return 0;
+}
+
+
+int
+test_mheap_main (unformat_input_t * input)
+{
+ int i, j, k, n_iterations;
+ void *h, *h_mem;
+ uword *objects = 0;
+ u32 objects_used, really_verbose, n_objects, max_object_size;
+ u32 check_mask, seed, trace, use_vm;
+ u32 print_every = 0;
+ u32 *data;
+ mheap_t *mh;
+
+ /* Validation flags. */
+ check_mask = 0;
+#define CHECK_VALIDITY 1
+#define CHECK_DATA 2
+#define CHECK_ALIGN 4
+#define TEST1 8
+
+ n_iterations = 10;
+ seed = 0;
+ max_object_size = 100;
+ n_objects = 1000;
+ trace = 0;
+ really_verbose = 0;
+ use_vm = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (0 == unformat (input, "iter %d", &n_iterations)
+ && 0 == unformat (input, "count %d", &n_objects)
+ && 0 == unformat (input, "size %d", &max_object_size)
+ && 0 == unformat (input, "seed %d", &seed)
+ && 0 == unformat (input, "print %d", &print_every)
+ && 0 == unformat (input, "validdata %|",
+ &check_mask, CHECK_DATA | CHECK_VALIDITY)
+ && 0 == unformat (input, "valid %|",
+ &check_mask, CHECK_VALIDITY)
+ && 0 == unformat (input, "verbose %=", &really_verbose, 1)
+ && 0 == unformat (input, "trace %=", &trace, 1)
+ && 0 == unformat (input, "vm %=", &use_vm, 1)
+ && 0 == unformat (input, "align %|", &check_mask, CHECK_ALIGN)
+ && 0 == unformat (input, "test1 %|", &check_mask, TEST1))
+ {
+ clib_warning ("unknown input `%U'", format_unformat_error, input);
+ return 1;
+ }
+ }
+
+ /* Zero seed means use default. */
+ if (!seed)
+ seed = random_default_seed ();
+
+ if (check_mask & TEST1)
+ {
+ return test1 ();
+ }
+
+ if_verbose
+ ("testing %d iterations, %d %saligned objects, max. size %d, seed %d",
+ n_iterations, n_objects, (check_mask & CHECK_ALIGN) ? "randomly " : "un",
+ max_object_size, seed);
+
+ vec_resize (objects, n_objects);
+ if (vec_bytes (objects) > 0) /* stupid warning be gone */
+ clib_memset (objects, ~0, vec_bytes (objects));
+ objects_used = 0;
+
+ /* Allocate initial heap. */
+ {
+ uword size =
+ max_pow2 (2 * n_objects * max_object_size * sizeof (data[0]));
+
+ h_mem = clib_mem_alloc (size);
+ if (!h_mem)
+ return 0;
+
+ h = mheap_alloc (h_mem, size);
+ }
+
+ if (trace)
+ mheap_trace (h, trace);
+
+ mh = mheap_header (h);
+
+ if (use_vm)
+ mh->flags &= ~MHEAP_FLAG_DISABLE_VM;
+ else
+ mh->flags |= MHEAP_FLAG_DISABLE_VM;
+
+ if (check_mask & CHECK_VALIDITY)
+ mh->flags |= MHEAP_FLAG_VALIDATE;
+
+ for (i = 0; i < n_iterations; i++)
+ {
+ while (1)
+ {
+ j = random_u32 (&seed) % vec_len (objects);
+ if (objects[j] != ~0 || i + objects_used < n_iterations)
+ break;
+ }
+
+ if (objects[j] != ~0)
+ {
+ mheap_put (h, objects[j]);
+ objects_used--;
+ objects[j] = ~0;
+ }
+ else
+ {
+ uword size, align, align_offset;
+
+ size = (random_u32 (&seed) % max_object_size) * sizeof (data[0]);
+ align = align_offset = 0;
+ if (check_mask & CHECK_ALIGN)
+ {
+ align = 1 << (random_u32 (&seed) % 10);
+ align_offset = round_pow2 (random_u32 (&seed) & (align - 1),
+ sizeof (u32));
+ }
+
+ h = mheap_get_aligned (h, size, align, align_offset, &objects[j]);
+
+ if (align > 0)
+ ASSERT (0 == ((objects[j] + align_offset) & (align - 1)));
+
+ ASSERT (objects[j] != ~0);
+ objects_used++;
+
+ /* Set newly allocated object with test data. */
+ if (check_mask & CHECK_DATA)
+ {
+ uword len;
+
+ data = (void *) h + objects[j];
+ len = mheap_len (h, data);
+
+ ASSERT (size <= mheap_data_bytes (h, objects[j]));
+
+ data[0] = len;
+ for (k = 1; k < len; k++)
+ data[k] = objects[j] + k;
+ }
+ }
+
+ /* Verify that all used objects have correct test data. */
+ if (check_mask & 2)
+ {
+ for (j = 0; j < vec_len (objects); j++)
+ if (objects[j] != ~0)
+ {
+ u32 *data = h + objects[j];
+ uword len = data[0];
+ for (k = 1; k < len; k++)
+ ASSERT (data[k] == objects[j] + k);
+ }
+ }
+ if (print_every != 0 && i > 0 && (i % print_every) == 0)
+ fformat (stderr, "iteration %d: %U\n", i, format_mheap, h,
+ really_verbose);
+ }
+
+ if (verbose)
+ fformat (stderr, "%U\n", format_mheap, h, really_verbose);
+ mheap_free (h);
+ clib_mem_free (h_mem);
+ vec_free (objects);
+
+ 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_mheap_main (&i);
+ unformat_free (&i);
+
+ return ret;
+}
+#endif /* CLIB_UNIX */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/extras/gomemif/WORKSPACE b/extras/gomemif/WORKSPACE
deleted file mode 100644
index 10741f5b7c0..00000000000
--- a/extras/gomemif/WORKSPACE
+++ /dev/null
@@ -1,35 +0,0 @@
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-http_archive(
- name = "io_bazel_rules_go",
- urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.21.2/rules_go-v0.21.2.tar.gz",
- "https://github.com/bazelbuild/rules_go/releases/download/v0.21.2/rules_go-v0.21.2.tar.gz",
- ],
- sha256 = "f99a9d76e972e0c8f935b2fe6d0d9d778f67c760c6d2400e23fc2e469016e2bd",
-)
-
-load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
-
-http_archive(
- name = "bazel_gazelle",
- sha256 = "86c6d481b3f7aedc1d60c1c211c6f76da282ae197c3b3160f54bd3a8f847896f",
- urls = [
- "https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/bazel-gazelle/releases/download/v0.19.1/bazel-gazelle-v0.19.1.tar.gz",
- "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.19.1/bazel-gazelle-v0.19.1.tar.gz",
- ],
-)
-
-go_rules_dependencies()
-
-go_register_toolchains()
-
-load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
-
-gazelle_dependencies()
-
-go_repository(
- name = "com_github_profile",
- importpath = "github.com/pkg/profile",
- commit = "acd64d450fd45fb2afa41f833f3788c8a7797219"
-)
diff --git a/extras/gomemif/examples/BUILD.bazel b/extras/gomemif/examples/BUILD.bazel
deleted file mode 100644
index a88b92e37f1..00000000000
--- a/extras/gomemif/examples/BUILD.bazel
+++ /dev/null
@@ -1,21 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary")
-
-go_binary(
- name = "responder",
- srcs = ["responder.go"],
- visibility = ["//visibility:public",],
- deps = [
- "//memif:memif",
- "@com_github_profile//:go_default_library",
- ],
-)
-
-go_binary(
- name = "bridge",
- srcs = ["bridge.go"],
- visibility = ["//visibility:public",],
- deps = [
- "//memif:memif",
- "@com_github_profile//:go_default_library",
- ],
-)
diff --git a/extras/gomemif/examples/bridge.go b/extras/gomemif/examples/bridge.go
deleted file mode 100644
index a192034e3be..00000000000
--- a/extras/gomemif/examples/bridge.go
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package main
-
-import (
- "bufio"
- "flag"
- "fmt"
- "os"
- "strings"
- "sync"
- "time"
-
- "github.com/pkg/profile"
- "memif"
-)
-
-func Disconnected(i *memif.Interface) error {
- fmt.Println("Disconnected: ", i.GetName())
-
- data, ok := i.GetPrivateData().(*interfaceData)
- if !ok {
- return fmt.Errorf("Invalid private data")
- }
- close(data.quitChan) // stop polling
- close(data.errChan)
- data.wg.Wait() // wait until polling stops, then continue disconnect
-
- return nil
-}
-
-func Connected(i *memif.Interface) error {
- fmt.Println("Connected: ", i.GetName())
-
- data, ok := i.GetPrivateData().(*interfaceData)
- if !ok {
- return fmt.Errorf("Invalid private data")
- }
- data.errChan = make(chan error, 1)
- data.quitChan = make(chan struct{}, 1)
- data.wg.Add(1)
-
- go func(errChan chan<- error, quitChan <-chan struct{}, wg *sync.WaitGroup) {
- defer wg.Done()
- // allocate packet buffer
- pkt := make([]byte, 2048)
- // get rx queue
- rxq0, err := i.GetRxQueue(0)
- if err != nil {
- errChan <- err
- return
- }
-
- // wait until both interfaces are connected
- for !data.bri.IsConnected() {
- time.Sleep(100 * time.Millisecond)
- }
-
- // get bridged interfaces tx queue
- txq0, err := data.bri.GetTxQueue(0)
- if err != nil {
- errChan <- err
- return
- }
- for {
- select {
- case <-quitChan: // channel closed
- return
- default:
- // read packet from shared memory
- pktLen, err := rxq0.ReadPacket(pkt)
- if pktLen > 0 {
- // FIXME: prevent packet write if interface is disconencted
- // write packet to shared memory
- txq0.WritePacket(pkt[:pktLen])
- } else if err != nil {
- errChan <- err
- return
- }
- }
- }
- }(data.errChan, data.quitChan, &data.wg)
-
- return nil
-}
-
-type interfaceData struct {
- errChan chan error
- quitChan chan struct{}
- wg sync.WaitGroup
- // bridged interface
- bri *memif.Interface
-}
-
-func interractiveHelp() {
- fmt.Println("help - print this help")
- fmt.Println("start - start connecting loop")
- fmt.Println("show - print interface details")
- fmt.Println("exit - exit the application")
-}
-
-func newMemifInterface(socket *memif.Socket, id uint32, isMaster bool, name string) (*memif.Interface, *interfaceData, error) {
- data := &interfaceData{}
- args := &memif.Arguments{
- Id: id,
- IsMaster: isMaster,
- ConnectedFunc: Connected,
- DisconnectedFunc: Disconnected,
- PrivateData: data,
- Name: name,
- }
-
- i, err := socket.NewInterface(args)
- if err != nil {
- return nil, nil, fmt.Errorf("Failed to create interface on socket %s: %s", socket.GetFilename(), err)
- }
-
- // slave attempts to connect to control socket
- // to handle control communication call socket.StartPolling()
- if !i.IsMaster() {
- fmt.Println(args.Name, ": Connecting to control socket...")
- for !i.IsConnecting() {
- err = i.RequestConnection()
- if err != nil {
- /* TODO: check for ECONNREFUSED errno
- * if error is ECONNREFUSED it may simply mean that master
- * interface is not up yet, use i.RequestConnection()
- */
- return nil, nil, fmt.Errorf("Faild to connect: ", err)
- }
- }
- }
-
- return i, data, nil
-}
-
-func printMemifInterfaceDetails(i *memif.Interface) {
- fmt.Println(i.GetName(), ":")
- fmt.Println("\trole: ", memif.RoleToString(i.IsMaster()))
- fmt.Println("\tid: ", i.GetId())
- link := "down"
- if i.IsConnected() {
- link = "up"
- }
- fmt.Println("\tlink: ", link)
- fmt.Println("\tremote: ", i.GetRemoteName())
- fmt.Println("\tpeer: ", i.GetPeerName())
- if i.IsConnected() {
- mc := i.GetMemoryConfig()
- fmt.Println("queue pairs: ", mc.NumQueuePairs)
- fmt.Println("ring size: ", (1 << mc.Log2RingSize))
- fmt.Println("buffer size: ", mc.PacketBufferSize)
- }
-}
-
-func main() {
- memifErrChan := make(chan error)
- exitChan := make(chan struct{})
- var i0, i1 *memif.Interface
- var d0, d1 *interfaceData
-
- cpuprof := flag.String("cpuprof", "", "cpu profiling output file")
- memprof := flag.String("memprof", "", "mem profiling output file")
- role := flag.String("role", "slave", "interface role")
- name := flag.String("name", "gomemif", "interface name")
- socketName := flag.String("socket", "", "control socket filename")
-
- flag.Parse()
-
- // profiling options
- if *cpuprof != "" {
- defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop()
- }
- if *memprof != "" {
- defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop()
- }
-
- // memif options
- var isMaster bool
- switch *role {
- case "slave":
- isMaster = false
- case "master":
- isMaster = true
- default:
- fmt.Println("Invalid role")
- return
- }
-
- // create memif socket
- socket, err := memif.NewSocket("gomemif_example", *socketName)
- if err != nil {
- fmt.Println("Failed to create socket: ", err)
- return
- }
-
- i0, d0, err = newMemifInterface(socket, 0, isMaster, *name)
- if err != nil {
- fmt.Println(err)
- goto exit
- }
-
- // TODO: update name
- i1, d1, err = newMemifInterface(socket, 1, isMaster, *name)
- if err != nil {
- fmt.Println(err)
- goto exit
- }
-
- // set up bridge
- d0.bri = i1
- d1.bri = i0
-
- // user input goroutine
- go func(exitChan chan<- struct{}) {
- reader := bufio.NewReader(os.Stdin)
- fmt.Println("GoMemif: Responder")
- fmt.Println("-----------------------")
- for {
- fmt.Print("gomemif# ")
- text, _ := reader.ReadString('\n')
- // convert CRLF to LF
- text = strings.Replace(text, "\n", "", -1)
- switch text {
- case "help":
- interractiveHelp()
- case "start":
- // start polling for events on this socket
- socket.StartPolling(memifErrChan)
- case "show":
- printMemifInterfaceDetails(i0)
- printMemifInterfaceDetails(i1)
- case "exit":
- err = socket.StopPolling()
- if err != nil {
- fmt.Println("Failed to stop polling: ", err)
- }
- close(exitChan)
- return
- default:
- fmt.Println("Unknown input")
- }
- }
- }(exitChan)
-
- // main loop
- for {
- select {
- case <-exitChan:
- goto exit
- case err, ok := <-memifErrChan:
- if ok {
- fmt.Println(err)
- }
- case err, ok := <-d0.errChan:
- if ok {
- fmt.Println(err)
- }
- case err, ok := <-d1.errChan:
- if ok {
- fmt.Println(err)
- }
- default:
- continue
- }
- }
-
-exit:
- socket.Delete()
- close(memifErrChan)
-}
diff --git a/extras/gomemif/examples/responder.go b/extras/gomemif/examples/responder.go
deleted file mode 100644
index 8fda6435bc5..00000000000
--- a/extras/gomemif/examples/responder.go
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package main
-
-import (
- "bufio"
- "flag"
- "fmt"
- "os"
- "strings"
- "sync"
-
- "github.com/pkg/profile"
- "memif"
-)
-
-func Disconnected(i *memif.Interface) error {
- fmt.Println("Disconnected: ", i.GetName())
-
- data, ok := i.GetPrivateData().(*interfaceData)
- if !ok {
- return fmt.Errorf("Invalid private data")
- }
- close(data.quitChan) // stop polling
- close(data.errChan)
- data.wg.Wait() // wait until polling stops, then continue disconnect
-
- return nil
-}
-
-func Connected(i *memif.Interface) error {
- fmt.Println("Connected: ", i.GetName())
-
- data, ok := i.GetPrivateData().(*interfaceData)
- if !ok {
- return fmt.Errorf("Invalid private data")
- }
- data.errChan = make(chan error, 1)
- data.quitChan = make(chan struct{}, 1)
- data.wg.Add(1)
-
- go func(errChan chan<- error, quitChan <-chan struct{}, wg *sync.WaitGroup) {
- defer wg.Done()
- // allocate packet buffer
- pkt := make([]byte, 2048)
- // get rx queue
- rxq0, err := i.GetRxQueue(0)
- if err != nil {
- errChan <- err
- return
- }
- // get tx queue
- txq0, err := i.GetTxQueue(0)
- if err != nil {
- errChan <- err
- return
- }
- for {
- select {
- case <-quitChan: // channel closed
- return
- default:
- // read packet from shared memory
- pktLen, err := rxq0.ReadPacket(pkt)
- if pktLen > 0 {
- // write packet to shared memory
- txq0.WritePacket(pkt[:pktLen])
- } else if err != nil {
- errChan <- err
- return
- }
- }
- }
- }(data.errChan, data.quitChan, &data.wg)
-
- return nil
-}
-
-type interfaceData struct {
- errChan chan error
- quitChan chan struct{}
- wg sync.WaitGroup
-}
-
-func interractiveHelp() {
- fmt.Println("help - print this help")
- fmt.Println("start - start connecting loop")
- fmt.Println("show - print interface details")
- fmt.Println("exit - exit the application")
-}
-
-func main() {
- cpuprof := flag.String("cpuprof", "", "cpu profiling output file")
- memprof := flag.String("memprof", "", "mem profiling output file")
- role := flag.String("role", "slave", "interface role")
- name := flag.String("name", "gomemif", "interface name")
- socketName := flag.String("socket", "", "control socket filename")
-
- flag.Parse()
-
- if *cpuprof != "" {
- defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop()
- }
- if *memprof != "" {
- defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop()
- }
-
- memifErrChan := make(chan error)
- exitChan := make(chan struct{})
-
- var isMaster bool
- switch *role {
- case "slave":
- isMaster = false
- case "master":
- isMaster = true
- default:
- fmt.Println("Invalid role")
- return
- }
-
- fmt.Println("GoMemif: Responder")
- fmt.Println("-----------------------")
-
- socket, err := memif.NewSocket("gomemif_example", *socketName)
- if err != nil {
- fmt.Println("Failed to create socket: ", err)
- return
- }
-
- data := interfaceData{}
- args := &memif.Arguments{
- IsMaster: isMaster,
- ConnectedFunc: Connected,
- DisconnectedFunc: Disconnected,
- PrivateData: &data,
- Name: *name,
- }
-
- i, err := socket.NewInterface(args)
- if err != nil {
- fmt.Println("Failed to create interface on socket %s: %s", socket.GetFilename(), err)
- goto exit
- }
-
- // slave attempts to connect to control socket
- // to handle control communication call socket.StartPolling()
- if !i.IsMaster() {
- fmt.Println(args.Name, ": Connecting to control socket...")
- for !i.IsConnecting() {
- err = i.RequestConnection()
- if err != nil {
- /* TODO: check for ECONNREFUSED errno
- * if error is ECONNREFUSED it may simply mean that master
- * interface is not up yet, use i.RequestConnection()
- */
- fmt.Println("Faild to connect: ", err)
- goto exit
- }
- }
- }
-
- go func(exitChan chan<- struct{}) {
- reader := bufio.NewReader(os.Stdin)
- for {
- fmt.Print("gomemif# ")
- text, _ := reader.ReadString('\n')
- // convert CRLF to LF
- text = strings.Replace(text, "\n", "", -1)
- switch text {
- case "help":
- interractiveHelp()
- case "start":
- // start polling for events on this socket
- socket.StartPolling(memifErrChan)
- case "show":
- fmt.Println("remote: ", i.GetRemoteName())
- fmt.Println("peer: ", i.GetPeerName())
- case "exit":
- err = socket.StopPolling()
- if err != nil {
- fmt.Println("Failed to stop polling: ", err)
- }
- close(exitChan)
- return
- default:
- fmt.Println("Unknown input")
- }
- }
- }(exitChan)
-
- for {
- select {
- case <-exitChan:
- goto exit
- case err, ok := <-memifErrChan:
- if ok {
- fmt.Println(err)
- }
- case err, ok := <-data.errChan:
- if ok {
- fmt.Println(err)
- }
- default:
- continue
- }
- }
-
-exit:
- socket.Delete()
- close(memifErrChan)
-}
diff --git a/extras/gomemif/memif/BUILD.bazel b/extras/gomemif/memif/BUILD.bazel
deleted file mode 100644
index e6539ff59bd..00000000000
--- a/extras/gomemif/memif/BUILD.bazel
+++ /dev/null
@@ -1,17 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
- name = "memif",
- srcs = [
- "interface.go",
- "interface_unsafe.go",
- "control_channel.go",
- "control_channel_unsafe.go",
- "memif.go",
- "memif_unsafe.go",
- "packet_writer.go",
- "packet_reader.go",
- ],
- importpath = "memif",
- visibility = ["//visibility:public",],
-)
diff --git a/extras/gomemif/memif/control_channel.go b/extras/gomemif/memif/control_channel.go
deleted file mode 100644
index 32e34933ab4..00000000000
--- a/extras/gomemif/memif/control_channel.go
+++ /dev/null
@@ -1,965 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-import (
- "bytes"
- "container/list"
- "encoding/binary"
- "fmt"
- "os"
- "sync"
- "syscall"
-)
-
-const maxEpollEvents = 1
-const maxControlLen = 256
-
-const errorFdNotFound = "fd not found"
-
-// controlMsg represents a message used in communication between memif peers
-type controlMsg struct {
- Buffer *bytes.Buffer
- Fd int
-}
-
-// listener represents a listener functionality of UNIX domain socket
-type listener struct {
- socket *Socket
- event syscall.EpollEvent
-}
-
-// controlChannel represents a communication channel between memif peers
-// backed by UNIX domain socket
-type controlChannel struct {
- listRef *list.Element
- socket *Socket
- i *Interface
- event syscall.EpollEvent
- data [msgSize]byte
- control [maxControlLen]byte
- controlLen int
- msgQueue []controlMsg
- isConnected bool
-}
-
-// Socket represents a UNIX domain socket used for communication
-// between memif peers
-type Socket struct {
- appName string
- filename string
- listener *listener
- interfaceList *list.List
- ccList *list.List
- epfd int
- wakeEvent syscall.EpollEvent
- stopPollChan chan struct{}
- wg sync.WaitGroup
-}
-
-// StopPolling stops polling events on the socket
-func (socket *Socket) StopPolling() error {
- if socket.stopPollChan != nil {
- // stop polling msg
- close(socket.stopPollChan)
- // wake epoll
- buf := make([]byte, 8)
- binary.PutUvarint(buf, 1)
- n, err := syscall.Write(int(socket.wakeEvent.Fd), buf[:])
- if err != nil {
- return err
- }
- if n != 8 {
- return fmt.Errorf("Faild to write to eventfd")
- }
- // wait until polling is stopped
- socket.wg.Wait()
- }
-
- return nil
-}
-
-// StartPolling starts polling and handling events on the socket,
-// enabling communication between memif peers
-func (socket *Socket) StartPolling(errChan chan<- error) {
- socket.stopPollChan = make(chan struct{})
- socket.wg.Add(1)
- go func() {
- var events [maxEpollEvents]syscall.EpollEvent
- defer socket.wg.Done()
-
- for {
- select {
- case <-socket.stopPollChan:
- return
- default:
- num, err := syscall.EpollWait(socket.epfd, events[:], -1)
- if err != nil {
- errChan <- fmt.Errorf("EpollWait: ", err)
- return
- }
-
- for ev := 0; ev < num; ev++ {
- if events[0].Fd == socket.wakeEvent.Fd {
- continue
- }
- err = socket.handleEvent(&events[0])
- if err != nil {
- errChan <- fmt.Errorf("handleEvent: ", err)
- }
- }
- }
- }
- }()
-}
-
-// addEvent adds event to epoll instance associated with the socket
-func (socket *Socket) addEvent(event *syscall.EpollEvent) error {
- err := syscall.EpollCtl(socket.epfd, syscall.EPOLL_CTL_ADD, int(event.Fd), event)
- if err != nil {
- return fmt.Errorf("EpollCtl: %s", err)
- }
- return nil
-}
-
-// addEvent deletes event to epoll instance associated with the socket
-func (socket *Socket) delEvent(event *syscall.EpollEvent) error {
- err := syscall.EpollCtl(socket.epfd, syscall.EPOLL_CTL_DEL, int(event.Fd), event)
- if err != nil {
- return fmt.Errorf("EpollCtl: %s", err)
- }
- return nil
-}
-
-// Delete deletes the socket
-func (socket *Socket) Delete() (err error) {
- for elt := socket.ccList.Front(); elt != nil; elt = elt.Next() {
- cc, ok := elt.Value.(*controlChannel)
- if ok {
- err = cc.close(true, "Socket deleted")
- if err != nil {
- return nil
- }
- }
- }
- for elt := socket.interfaceList.Front(); elt != nil; elt = elt.Next() {
- i, ok := elt.Value.(*Interface)
- if ok {
- err = i.Delete()
- if err != nil {
- return err
- }
- }
- }
-
- if socket.listener != nil {
- err = socket.listener.close()
- if err != nil {
- return err
- }
- err = os.Remove(socket.filename)
- if err != nil {
- return nil
- }
- }
-
- err = socket.delEvent(&socket.wakeEvent)
- if err != nil {
- return fmt.Errorf("Failed to delete event: ", err)
- }
-
- syscall.Close(socket.epfd)
-
- return nil
-}
-
-// NewSocket returns a new Socket
-func NewSocket(appName string, filename string) (socket *Socket, err error) {
- socket = &Socket{
- appName: appName,
- filename: filename,
- interfaceList: list.New(),
- ccList: list.New(),
- }
- if socket.filename == "" {
- socket.filename = DefaultSocketFilename
- }
-
- socket.epfd, _ = syscall.EpollCreate1(0)
-
- efd, err := eventFd()
- socket.wakeEvent = syscall.EpollEvent{
- Events: syscall.EPOLLIN | syscall.EPOLLERR | syscall.EPOLLHUP,
- Fd: int32(efd),
- }
- err = socket.addEvent(&socket.wakeEvent)
- if err != nil {
- return nil, fmt.Errorf("Failed to add event: ", err)
- }
-
- return socket, nil
-}
-
-// handleEvent handles epoll event
-func (socket *Socket) handleEvent(event *syscall.EpollEvent) error {
- if socket.listener != nil && socket.listener.event.Fd == event.Fd {
- return socket.listener.handleEvent(event)
- }
-
- for elt := socket.ccList.Front(); elt != nil; elt = elt.Next() {
- cc, ok := elt.Value.(*controlChannel)
- if ok {
- if cc.event.Fd == event.Fd {
- return cc.handleEvent(event)
- }
- }
- }
-
- return fmt.Errorf(errorFdNotFound)
-}
-
-// handleEvent handles epoll event for listener
-func (l *listener) handleEvent(event *syscall.EpollEvent) error {
- // hang up
- if (event.Events & syscall.EPOLLHUP) == syscall.EPOLLHUP {
- err := l.close()
- if err != nil {
- return fmt.Errorf("Failed to close listener after hang up event: ", err)
- }
- return fmt.Errorf("Hang up: ", l.socket.filename)
- }
-
- // error
- if (event.Events & syscall.EPOLLERR) == syscall.EPOLLERR {
- err := l.close()
- if err != nil {
- return fmt.Errorf("Failed to close listener after receiving an error event: ", err)
- }
- return fmt.Errorf("Received error event on listener ", l.socket.filename)
- }
-
- // read message
- if (event.Events & syscall.EPOLLIN) == syscall.EPOLLIN {
- newFd, _, err := syscall.Accept(int(l.event.Fd))
- if err != nil {
- return fmt.Errorf("Accept: %s", err)
- }
-
- cc, err := l.socket.addControlChannel(newFd, nil)
- if err != nil {
- return fmt.Errorf("Failed to add control channel: %s", err)
- }
-
- err = cc.msgEnqHello()
- if err != nil {
- return fmt.Errorf("msgEnqHello: %s", err)
- }
-
- err = cc.sendMsg()
- if err != nil {
- return err
- }
-
- return nil
- }
-
- return fmt.Errorf("Unexpected event: ", event.Events)
-}
-
-// handleEvent handles epoll event for control channel
-func (cc *controlChannel) handleEvent(event *syscall.EpollEvent) error {
- var size int
- var err error
-
- // hang up
- if (event.Events & syscall.EPOLLHUP) == syscall.EPOLLHUP {
- // close cc, don't send msg
- err := cc.close(false, "")
- if err != nil {
- return fmt.Errorf("Failed to close control channel after hang up event: ", err)
- }
- return fmt.Errorf("Hang up: ", cc.i.GetName())
- }
-
- if (event.Events & syscall.EPOLLERR) == syscall.EPOLLERR {
- // close cc, don't send msg
- err := cc.close(false, "")
- if err != nil {
- return fmt.Errorf("Failed to close control channel after receiving an error event: ", err)
- }
- return fmt.Errorf("Received error event on control channel ", cc.i.GetName())
- }
-
- if (event.Events & syscall.EPOLLIN) == syscall.EPOLLIN {
- size, cc.controlLen, _, _, err = syscall.Recvmsg(int(cc.event.Fd), cc.data[:], cc.control[:], 0)
- if err != nil {
- return fmt.Errorf("recvmsg: %s", err)
- }
- if size != msgSize {
- return fmt.Errorf("invalid message size %d", size)
- }
-
- err = cc.parseMsg()
- if err != nil {
- return err
- }
-
- err = cc.sendMsg()
- if err != nil {
- return err
- }
-
- return nil
- }
-
- return fmt.Errorf("Unexpected event: ", event.Events)
-}
-
-// close closes the listener
-func (l *listener) close() error {
- err := l.socket.delEvent(&l.event)
- if err != nil {
- return fmt.Errorf("Failed to del event: ", err)
- }
- err = syscall.Close(int(l.event.Fd))
- if err != nil {
- return fmt.Errorf("Failed to close socket: ", err)
- }
- return nil
-}
-
-// AddListener adds a lisntener to the socket. The fd must describe a
-// UNIX domain socket already bound to a UNIX domain filename and
-// marked as listener
-func (socket *Socket) AddListener(fd int) (err error) {
- l := &listener{
- // we will need this to look up master interface by id
- socket: socket,
- }
-
- l.event = syscall.EpollEvent{
- Events: syscall.EPOLLIN | syscall.EPOLLERR | syscall.EPOLLHUP,
- Fd: int32(fd),
- }
- err = socket.addEvent(&l.event)
- if err != nil {
- return fmt.Errorf("Failed to add event: ", err)
- }
-
- socket.listener = l
-
- return nil
-}
-
-// addListener creates new UNIX domain socket, binds it to the address
-// and marks it as listener
-func (socket *Socket) addListener() (err error) {
- // create socket
- fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
- if err != nil {
- return fmt.Errorf("Failed to create UNIX domain socket")
- }
- usa := &syscall.SockaddrUnix{Name: socket.filename}
-
- // Bind to address and start listening
- err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)
- if err != nil {
- return fmt.Errorf("Failed to set socket option %s : %v", socket.filename, err)
- }
- err = syscall.Bind(fd, usa)
- if err != nil {
- return fmt.Errorf("Failed to bind socket %s : %v", socket.filename, err)
- }
- err = syscall.Listen(fd, syscall.SOMAXCONN)
- if err != nil {
- return fmt.Errorf("Failed to listen on socket %s : %v", socket.filename, err)
- }
-
- return socket.AddListener(fd)
-}
-
-// close closes a control channel, if the control channel is assigned an
-// interface, the interface is disconnected
-func (cc *controlChannel) close(sendMsg bool, str string) (err error) {
- if sendMsg == true {
- // first clear message queue so that the disconnect
- // message is the only message in queue
- cc.msgQueue = []controlMsg{}
- cc.msgEnqDisconnect(str)
-
- err = cc.sendMsg()
- if err != nil {
- return err
- }
- }
-
- err = cc.socket.delEvent(&cc.event)
- if err != nil {
- return fmt.Errorf("Failed to del event: ", err)
- }
-
- // remove referance form socket
- cc.socket.ccList.Remove(cc.listRef)
-
- if cc.i != nil {
- err = cc.i.disconnect()
- if err != nil {
- return fmt.Errorf("Interface Disconnect: ", err)
- }
- }
-
- return nil
-}
-
-//addControlChannel returns a new controlChannel and adds it to the socket
-func (socket *Socket) addControlChannel(fd int, i *Interface) (*controlChannel, error) {
- cc := &controlChannel{
- socket: socket,
- i: i,
- isConnected: false,
- }
-
- var err error
-
- cc.event = syscall.EpollEvent{
- Events: syscall.EPOLLIN | syscall.EPOLLERR | syscall.EPOLLHUP,
- Fd: int32(fd),
- }
- err = socket.addEvent(&cc.event)
- if err != nil {
- return nil, fmt.Errorf("Failed to add event: ", err)
- }
-
- cc.listRef = socket.ccList.PushBack(cc)
-
- return cc, nil
-}
-
-func (cc *controlChannel) msgEnqAck() (err error) {
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeAck)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: -1,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) msgEnqHello() (err error) {
- hello := MsgHello{
- VersionMin: Version,
- VersionMax: Version,
- MaxRegion: 255,
- MaxRingM2S: 255,
- MaxRingS2M: 255,
- MaxLog2RingSize: 14,
- }
-
- copy(hello.Name[:], []byte(cc.socket.appName))
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeHello)
- err = binary.Write(buf, binary.LittleEndian, hello)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: -1,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseHello() (err error) {
- var hello MsgHello
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &hello)
- if err != nil {
- return
- }
-
- if hello.VersionMin > Version || hello.VersionMax < Version {
- return fmt.Errorf("Incompatible memif version")
- }
-
- cc.i.run = cc.i.args.MemoryConfig
-
- cc.i.run.NumQueuePairs = min16(cc.i.args.MemoryConfig.NumQueuePairs, hello.MaxRingS2M)
- cc.i.run.NumQueuePairs = min16(cc.i.args.MemoryConfig.NumQueuePairs, hello.MaxRingM2S)
- cc.i.run.Log2RingSize = min8(cc.i.args.MemoryConfig.Log2RingSize, hello.MaxLog2RingSize)
-
- cc.i.remoteName = string(hello.Name[:])
-
- return nil
-}
-
-func (cc *controlChannel) msgEnqInit() (err error) {
- init := MsgInit{
- Version: Version,
- Id: cc.i.args.Id,
- Mode: interfaceModeEthernet,
- }
-
- copy(init.Name[:], []byte(cc.socket.appName))
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeInit)
- err = binary.Write(buf, binary.LittleEndian, init)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: -1,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseInit() (err error) {
- var init MsgInit
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &init)
- if err != nil {
- return
- }
-
- if init.Version != Version {
- return fmt.Errorf("Incompatible memif driver version")
- }
-
- // find peer interface
- for elt := cc.socket.interfaceList.Front(); elt != nil; elt = elt.Next() {
- i, ok := elt.Value.(*Interface)
- if ok {
- if i.args.Id == init.Id && i.args.IsMaster && i.cc == nil {
- // verify secret
- if i.args.Secret != init.Secret {
- return fmt.Errorf("Invalid secret")
- }
- // interface is assigned to control channel
- i.cc = cc
- cc.i = i
- cc.i.run = cc.i.args.MemoryConfig
- cc.i.remoteName = string(init.Name[:])
-
- return nil
- }
- }
- }
-
- return fmt.Errorf("Invalid interface id")
-}
-
-func (cc *controlChannel) msgEnqAddRegion(regionIndex uint16) (err error) {
- if len(cc.i.regions) <= int(regionIndex) {
- return fmt.Errorf("Invalid region index")
- }
-
- addRegion := MsgAddRegion{
- Index: regionIndex,
- Size: cc.i.regions[regionIndex].size,
- }
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeAddRegion)
- err = binary.Write(buf, binary.LittleEndian, addRegion)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: cc.i.regions[regionIndex].fd,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseAddRegion() (err error) {
- var addRegion MsgAddRegion
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &addRegion)
- if err != nil {
- return
- }
-
- fd, err := cc.parseControlMsg()
- if err != nil {
- return fmt.Errorf("parseControlMsg: %s", err)
- }
-
- if addRegion.Index > 255 {
- return fmt.Errorf("Invalid memory region index")
- }
-
- region := memoryRegion{
- size: addRegion.Size,
- fd: fd,
- }
-
- cc.i.regions = append(cc.i.regions, region)
-
- return nil
-}
-
-func (cc *controlChannel) msgEnqAddRing(ringType ringType, ringIndex uint16) (err error) {
- var q Queue
- var flags uint16 = 0
-
- if ringType == ringTypeS2M {
- q = cc.i.txQueues[ringIndex]
- flags = msgAddRingFlagS2M
- } else {
- q = cc.i.rxQueues[ringIndex]
- }
-
- addRing := MsgAddRing{
- Index: ringIndex,
- Offset: uint32(q.ring.offset),
- Region: uint16(q.ring.region),
- RingSizeLog2: uint8(q.ring.log2Size),
- Flags: flags,
- PrivateHdrSize: 0,
- }
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeAddRing)
- err = binary.Write(buf, binary.LittleEndian, addRing)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: q.interruptFd,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseAddRing() (err error) {
- var addRing MsgAddRing
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &addRing)
- if err != nil {
- return
- }
-
- fd, err := cc.parseControlMsg()
- if err != nil {
- return err
- }
-
- if addRing.Index >= cc.i.run.NumQueuePairs {
- return fmt.Errorf("invalid ring index")
- }
-
- q := Queue{
- i: cc.i,
- interruptFd: fd,
- }
-
- if (addRing.Flags & msgAddRingFlagS2M) == msgAddRingFlagS2M {
- q.ring = newRing(int(addRing.Region), ringTypeS2M, int(addRing.Offset), int(addRing.RingSizeLog2))
- cc.i.rxQueues = append(cc.i.rxQueues, q)
- } else {
- q.ring = newRing(int(addRing.Region), ringTypeM2S, int(addRing.Offset), int(addRing.RingSizeLog2))
- cc.i.txQueues = append(cc.i.txQueues, q)
- }
-
- return nil
-}
-
-func (cc *controlChannel) msgEnqConnect() (err error) {
- var connect MsgConnect
- copy(connect.Name[:], []byte(cc.i.args.Name))
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeConnect)
- err = binary.Write(buf, binary.LittleEndian, connect)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: -1,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseConnect() (err error) {
- var connect MsgConnect
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &connect)
- if err != nil {
- return
- }
-
- cc.i.peerName = string(connect.Name[:])
-
- err = cc.i.connect()
- if err != nil {
- return err
- }
-
- cc.isConnected = true
-
- return nil
-}
-
-func (cc *controlChannel) msgEnqConnected() (err error) {
- var connected MsgConnected
- copy(connected.Name[:], []byte(cc.i.args.Name))
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeConnected)
- err = binary.Write(buf, binary.LittleEndian, connected)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: -1,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseConnected() (err error) {
- var conn MsgConnected
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &conn)
- if err != nil {
- return
- }
-
- cc.i.peerName = string(conn.Name[:])
-
- err = cc.i.connect()
- if err != nil {
- return err
- }
-
- cc.isConnected = true
-
- return nil
-}
-
-func (cc *controlChannel) msgEnqDisconnect(str string) (err error) {
- dc := MsgDisconnect{
- // not implemented
- Code: 0,
- }
- copy(dc.String[:], str)
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.LittleEndian, msgTypeDisconnect)
- err = binary.Write(buf, binary.LittleEndian, dc)
-
- msg := controlMsg{
- Buffer: buf,
- Fd: -1,
- }
-
- cc.msgQueue = append(cc.msgQueue, msg)
-
- return nil
-}
-
-func (cc *controlChannel) parseDisconnect() (err error) {
- var dc MsgDisconnect
-
- buf := bytes.NewReader(cc.data[msgTypeSize:])
- err = binary.Read(buf, binary.LittleEndian, &dc)
- if err != nil {
- return
- }
-
- err = cc.close(false, string(dc.String[:]))
- if err != nil {
- return fmt.Errorf("Failed to disconnect control channel: ", err)
- }
-
- return nil
-}
-
-func (cc *controlChannel) parseMsg() error {
- var msgType msgType
- var err error
-
- buf := bytes.NewReader(cc.data[:])
- err = binary.Read(buf, binary.LittleEndian, &msgType)
-
- if msgType == msgTypeAck {
- return nil
- } else if msgType == msgTypeHello {
- // Configure
- err = cc.parseHello()
- if err != nil {
- goto error
- }
- // Initialize slave memif
- err = cc.i.initializeRegions()
- if err != nil {
- goto error
- }
- err = cc.i.initializeQueues()
- if err != nil {
- goto error
- }
- // Enqueue messages
- err = cc.msgEnqInit()
- if err != nil {
- goto error
- }
- for i := 0; i < len(cc.i.regions); i++ {
- err = cc.msgEnqAddRegion(uint16(i))
- if err != nil {
- goto error
- }
- }
- for i := 0; uint16(i) < cc.i.run.NumQueuePairs; i++ {
- err = cc.msgEnqAddRing(ringTypeS2M, uint16(i))
- if err != nil {
- goto error
- }
- }
- for i := 0; uint16(i) < cc.i.run.NumQueuePairs; i++ {
- err = cc.msgEnqAddRing(ringTypeM2S, uint16(i))
- if err != nil {
- goto error
- }
- }
- err = cc.msgEnqConnect()
- if err != nil {
- goto error
- }
- } else if msgType == msgTypeInit {
- err = cc.parseInit()
- if err != nil {
- goto error
- }
-
- err = cc.msgEnqAck()
- if err != nil {
- goto error
- }
- } else if msgType == msgTypeAddRegion {
- err = cc.parseAddRegion()
- if err != nil {
- goto error
- }
-
- err = cc.msgEnqAck()
- if err != nil {
- goto error
- }
- } else if msgType == msgTypeAddRing {
- err = cc.parseAddRing()
- if err != nil {
- goto error
- }
-
- err = cc.msgEnqAck()
- if err != nil {
- goto error
- }
- } else if msgType == msgTypeConnect {
- err = cc.parseConnect()
- if err != nil {
- goto error
- }
-
- err = cc.msgEnqConnected()
- if err != nil {
- goto error
- }
- } else if msgType == msgTypeConnected {
- err = cc.parseConnected()
- if err != nil {
- goto error
- }
- } else if msgType == msgTypeDisconnect {
- err = cc.parseDisconnect()
- if err != nil {
- goto error
- }
- } else {
- err = fmt.Errorf("unknown message %d", msgType)
- goto error
- }
-
- return nil
-
-error:
- err1 := cc.close(true, err.Error())
- if err1 != nil {
- return fmt.Errorf(err.Error(), ": Failed to close control channel: ", err1)
- }
-
- return err
-}
-
-// parseControlMsg parses control message and returns file descriptor
-// if any
-func (cc *controlChannel) parseControlMsg() (fd int, err error) {
- // Assert only called when we require FD
- fd = -1
-
- controlMsgs, err := syscall.ParseSocketControlMessage(cc.control[:cc.controlLen])
- if err != nil {
- return -1, fmt.Errorf("syscall.ParseSocketControlMessage: %s", err)
- }
-
- if len(controlMsgs) == 0 {
- return -1, fmt.Errorf("Missing control message")
- }
-
- for _, cmsg := range controlMsgs {
- if cmsg.Header.Level == syscall.SOL_SOCKET {
- if cmsg.Header.Type == syscall.SCM_RIGHTS {
- FDs, err := syscall.ParseUnixRights(&cmsg)
- if err != nil {
- return -1, fmt.Errorf("syscall.ParseUnixRights: %s", err)
- }
- if len(FDs) == 0 {
- continue
- }
- // Only expect single FD
- fd = FDs[0]
- }
- }
- }
-
- if fd == -1 {
- return -1, fmt.Errorf("Missing file descriptor")
- }
-
- return fd, nil
-}
diff --git a/extras/gomemif/memif/control_channel_unsafe.go b/extras/gomemif/memif/control_channel_unsafe.go
deleted file mode 100644
index 9e91297b160..00000000000
--- a/extras/gomemif/memif/control_channel_unsafe.go
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-import (
- "fmt"
- "os"
- "syscall"
- "unsafe"
-)
-
-// sendMsg sends a control message from contorl channels message queue
-func (cc *controlChannel) sendMsg() (err error) {
- if len(cc.msgQueue) < 1 {
- return nil
- }
- // Get message buffer
- msg := cc.msgQueue[0]
- // Dequeue
- cc.msgQueue = cc.msgQueue[1:]
-
- iov := &syscall.Iovec{
- Base: &msg.Buffer.Bytes()[0],
- Len: msgSize,
- }
-
- msgh := syscall.Msghdr{
- Iov: iov,
- Iovlen: 1,
- }
-
- if msg.Fd > 0 {
- oob := syscall.UnixRights(msg.Fd)
- msgh.Control = &oob[0]
- msgh.Controllen = uint64(syscall.CmsgSpace(4))
- }
-
- _, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, uintptr(cc.event.Fd), uintptr(unsafe.Pointer(&msgh)), uintptr(0))
- if errno != 0 {
- err = os.NewSyscallError("sendmsg", errno)
- return fmt.Errorf("SYS_SENDMSG: %s", errno)
- }
-
- return nil
-}
diff --git a/extras/gomemif/memif/interface.go b/extras/gomemif/memif/interface.go
deleted file mode 100644
index a571deb43c9..00000000000
--- a/extras/gomemif/memif/interface.go
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-// Package memif provides the implementation of shared memory interface (memif).
-//
-// Memif network interfaces communicate using UNIX domain socket. This socket
-// must be first created using NewSocket(). Then interfaces can be added
-// to this socket using NewInterface(). To start communication on each socket
-// socket.StartPolling() must be called. socket.StopPolling() will stop
-// the communication. When the interface changes link status Connected and
-// Disconencted callbacks set in Arguments for each interface are called
-// respectively. Once the interface is connected rx and tx queues can be
-// aquired using interface.GetRxQueue() and interface.GetTxQueue().
-// Packets can be transmitted by calling queue.ReadPacket() on rx queues and
-// queue.WritePacket() on tx queues. If the interface is disconnected
-// queue.ReadPacket() and queue.WritePacket() MUST not be called.
-//
-// Data transmission is backed by shared memory. The driver works in
-// promiscuous mode only.
-package memif
-
-import (
- "container/list"
- "fmt"
- "os"
- "syscall"
-)
-
-const (
- DefaultSocketFilename = "/run/vpp/memif.sock"
- DefaultNumQueuePairs = 1
- DefaultLog2RingSize = 10
- DefaultPacketBufferSize = 2048
-)
-
-const mfd_allow_sealing = 2
-const sys_memfd_create = 319
-const f_add_seals = 1033
-const f_seal_shrink = 0x0002
-
-const efd_nonblock = 04000
-
-// ConnectedFunc is a callback called when an interface is connected
-type ConnectedFunc func(i *Interface) error
-
-// DisconnectedFunc is a callback called when an interface is disconnected
-type DisconnectedFunc func(i *Interface) error
-
-// MemoryConfig represents shared memory configuration
-type MemoryConfig struct {
- NumQueuePairs uint16 // number of queue pairs
- Log2RingSize uint8 // ring size as log2
- PacketBufferSize uint32 // size of single packet buffer
-}
-
-// Arguments represent interface configuration
-type Arguments struct {
- Id uint32 // Interface identifier unique across socket. Used to identify peer interface when connecting
- IsMaster bool // Interface role master/slave
- Name string
- Secret [24]byte // optional parameter, secrets of the interfaces must match if they are to connect
- MemoryConfig MemoryConfig
- ConnectedFunc ConnectedFunc // callback called when interface changes status to connected
- DisconnectedFunc DisconnectedFunc // callback called when interface changes status to disconnected
- PrivateData interface{} // private data used by client program
-}
-
-// memoryRegion represents a shared memory mapped file
-type memoryRegion struct {
- data []byte
- size uint64
- fd int
- packetBufferOffset uint32
-}
-
-// Queue represents rx or tx queue
-type Queue struct {
- ring *ring
- i *Interface
- lastHead uint16
- lastTail uint16
- interruptFd int
-}
-
-// Interface represents memif network interface
-type Interface struct {
- args Arguments
- run MemoryConfig
- privateData interface{}
- listRef *list.Element
- socket *Socket
- cc *controlChannel
- remoteName string
- peerName string
- regions []memoryRegion
- txQueues []Queue
- rxQueues []Queue
-}
-
-// IsMaster returns true if the interfaces role is master, else returns false
-func (i *Interface) IsMaster() bool {
- return i.args.IsMaster
-}
-
-// GetRemoteName returns the name of the application on which the peer
-// interface exists
-func (i *Interface) GetRemoteName() string {
- return i.remoteName
-}
-
-// GetPeerName returns peer interfaces name
-func (i *Interface) GetPeerName() string {
- return i.peerName
-}
-
-// GetName returens interfaces name
-func (i *Interface) GetName() string {
- return i.args.Name
-}
-
-// GetMemoryConfig returns interfaces active memory config.
-// If interface is not connected the config is invalid.
-func (i *Interface) GetMemoryConfig() MemoryConfig {
- return i.run
-}
-
-// GetRxQueue returns an rx queue specified by queue index
-func (i *Interface) GetRxQueue(qid int) (*Queue, error) {
- if qid >= len(i.rxQueues) {
- return nil, fmt.Errorf("Invalid Queue index")
- }
- return &i.rxQueues[qid], nil
-}
-
-// GetRxQueue returns a tx queue specified by queue index
-func (i *Interface) GetTxQueue(qid int) (*Queue, error) {
- if qid >= len(i.txQueues) {
- return nil, fmt.Errorf("Invalid Queue index")
- }
- return &i.txQueues[qid], nil
-}
-
-// GetEventFd returns queues interrupt event fd
-func (q *Queue) GetEventFd() (int, error) {
- return q.interruptFd, nil
-}
-
-// GetFilename returns sockets filename
-func (socket *Socket) GetFilename() string {
- return socket.filename
-}
-
-// close closes the queue
-func (q *Queue) close() {
- syscall.Close(q.interruptFd)
-}
-
-// IsConnecting returns true if the interface is connecting
-func (i *Interface) IsConnecting() bool {
- if i.cc != nil {
- return true
- }
- return false
-}
-
-// IsConnected returns true if the interface is connected
-func (i *Interface) IsConnected() bool {
- if i.cc != nil && i.cc.isConnected {
- return true
- }
- return false
-}
-
-// Disconnect disconnects the interface
-func (i *Interface) Disconnect() (err error) {
- if i.cc != nil {
- // close control and disconenct interface
- return i.cc.close(true, "Interface disconnected")
- }
- return nil
-}
-
-// disconnect finalizes interface disconnection
-func (i *Interface) disconnect() (err error) {
- if i.cc == nil { // disconnected
- return nil
- }
-
- err = i.args.DisconnectedFunc(i)
- if err != nil {
- return fmt.Errorf("DisconnectedFunc: ", err)
- }
-
- for _, q := range i.txQueues {
- q.close()
- }
- i.txQueues = []Queue{}
-
- for _, q := range i.rxQueues {
- q.close()
- }
- i.rxQueues = []Queue{}
-
- // unmap regions
- for _, r := range i.regions {
- err = syscall.Munmap(r.data)
- if err != nil {
- return err
- }
- err = syscall.Close(r.fd)
- if err != nil {
- return err
- }
- }
- i.regions = nil
- i.cc = nil
-
- i.peerName = ""
- i.remoteName = ""
-
- return nil
-}
-
-// Delete deletes the interface
-func (i *Interface) Delete() (err error) {
- i.Disconnect()
-
- // remove referance on socket
- i.socket.interfaceList.Remove(i.listRef)
- i = nil
-
- return nil
-}
-
-// GetSocket returns the socket the interface belongs to
-func (i *Interface) GetSocket() *Socket {
- return i.socket
-}
-
-// GetPrivateDate returns interfaces private data
-func (i *Interface) GetPrivateData() interface{} {
- return i.args.PrivateData
-}
-
-// GetId returns interfaces id
-func (i *Interface) GetId() uint32 {
- return i.args.Id
-}
-
-// RoleToString returns 'Master' if isMaster os true, else returns 'Slave'
-func RoleToString(isMaster bool) string {
- if isMaster {
- return "Master"
- }
- return "Slave"
-}
-
-// RequestConnection is used by slave interface to connect to a socket and
-// create a control channel
-func (i *Interface) RequestConnection() error {
- if i.IsMaster() {
- return fmt.Errorf("Only slave can request connection")
- }
- // create socket
- fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
- if err != nil {
- return fmt.Errorf("Failed to create UNIX domain socket: %v", err)
- }
- usa := &syscall.SockaddrUnix{Name: i.socket.filename}
-
- // Connect to listener socket
- err = syscall.Connect(fd, usa)
- if err != nil {
- return fmt.Errorf("Failed to connect socket %s : %v", i.socket.filename, err)
- }
-
- // Create control channel
- i.cc, err = i.socket.addControlChannel(fd, i)
- if err != nil {
- return fmt.Errorf("Failed to create control channel: %v", err)
- }
-
- return nil
-}
-
-// NewInterface returns a new memif network interface. When creating an interface
-// it's id must be unique across socket with the exception of loopback interface
-// in which case the id is the same but role differs
-func (socket *Socket) NewInterface(args *Arguments) (*Interface, error) {
- var err error
- // make sure the ID is unique on this socket
- for elt := socket.interfaceList.Front(); elt != nil; elt = elt.Next() {
- i, ok := elt.Value.(*Interface)
- if ok {
- if i.args.Id == args.Id && i.args.IsMaster == args.IsMaster {
- return nil, fmt.Errorf("Interface with id %u role %s already exists on this socket", args.Id, RoleToString(args.IsMaster))
- }
- }
- }
-
- // copy interface configuration
- i := Interface{
- args: *args,
- }
- // set default values
- if i.args.MemoryConfig.NumQueuePairs == 0 {
- i.args.MemoryConfig.NumQueuePairs = DefaultNumQueuePairs
- }
- if i.args.MemoryConfig.Log2RingSize == 0 {
- i.args.MemoryConfig.Log2RingSize = DefaultLog2RingSize
- }
- if i.args.MemoryConfig.PacketBufferSize == 0 {
- i.args.MemoryConfig.PacketBufferSize = DefaultPacketBufferSize
- }
-
- i.socket = socket
-
- // append interface to the list
- i.listRef = socket.interfaceList.PushBack(&i)
-
- if i.args.IsMaster {
- if socket.listener == nil {
- err = socket.addListener()
- if err != nil {
- return nil, fmt.Errorf("Failed to create listener channel: %s", err)
- }
- }
- }
-
- return &i, nil
-}
-
-// eventFd returns an eventfd (SYS_EVENTFD2)
-func eventFd() (efd int, err error) {
- u_efd, _, errno := syscall.Syscall(syscall.SYS_EVENTFD2, uintptr(0), uintptr(efd_nonblock), 0)
- if errno != 0 {
- return -1, os.NewSyscallError("eventfd", errno)
- }
- return int(u_efd), nil
-}
-
-// addRegions creates and adds a new memory region to the interface (slave only)
-func (i *Interface) addRegion(hasPacketBuffers bool, hasRings bool) (err error) {
- var r memoryRegion
-
- if hasRings {
- r.packetBufferOffset = uint32((i.run.NumQueuePairs + i.run.NumQueuePairs) * (ringSize + descSize*(1<<i.run.Log2RingSize)))
- } else {
- r.packetBufferOffset = 0
- }
-
- if hasPacketBuffers {
- r.size = uint64(r.packetBufferOffset + i.run.PacketBufferSize*uint32(1<<i.run.Log2RingSize)*uint32(i.run.NumQueuePairs+i.run.NumQueuePairs))
- } else {
- r.size = uint64(r.packetBufferOffset)
- }
-
- r.fd, err = memfdCreate()
- if err != nil {
- return err
- }
-
- _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(r.fd), uintptr(f_add_seals), uintptr(f_seal_shrink))
- if errno != 0 {
- syscall.Close(r.fd)
- return fmt.Errorf("memfdCreate: %s", os.NewSyscallError("fcntl", errno))
- }
-
- err = syscall.Ftruncate(r.fd, int64(r.size))
- if err != nil {
- syscall.Close(r.fd)
- r.fd = -1
- return fmt.Errorf("memfdCreate: %s", err)
- }
-
- r.data, err = syscall.Mmap(r.fd, 0, int(r.size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
- if err != nil {
- return fmt.Errorf("addRegion: %s", err)
- }
-
- i.regions = append(i.regions, r)
-
- return nil
-}
-
-// initializeRegions initializes interfaces regions (slave only)
-func (i *Interface) initializeRegions() (err error) {
-
- err = i.addRegion(true, true)
- if err != nil {
- return fmt.Errorf("initializeRegions: %s", err)
- }
-
- return nil
-}
-
-// initializeQueues initializes interfaces queues (slave only)
-func (i *Interface) initializeQueues() (err error) {
- var q *Queue
- var desc descBuf
- var slot int
-
- desc = newDescBuf()
- desc.setFlags(0)
- desc.setRegion(0)
- desc.setLength(int(i.run.PacketBufferSize))
-
- for qid := 0; qid < int(i.run.NumQueuePairs); qid++ {
- /* TX */
- q = &Queue{
- ring: i.newRing(0, ringTypeS2M, qid),
- lastHead: 0,
- lastTail: 0,
- i: i,
- }
- q.ring.setCookie(cookie)
- q.ring.setFlags(1)
- q.interruptFd, err = eventFd()
- if err != nil {
- return err
- }
- q.putRing()
- i.txQueues = append(i.txQueues, *q)
-
- for j := 0; j < q.ring.size; j++ {
- slot = qid*q.ring.size + j
- desc.setOffset(int(i.regions[0].packetBufferOffset + uint32(slot)*i.run.PacketBufferSize))
- q.putDescBuf(slot, desc)
- }
- }
- for qid := 0; qid < int(i.run.NumQueuePairs); qid++ {
- /* RX */
- q = &Queue{
- ring: i.newRing(0, ringTypeM2S, qid),
- lastHead: 0,
- lastTail: 0,
- i: i,
- }
- q.ring.setCookie(cookie)
- q.ring.setFlags(1)
- q.interruptFd, err = eventFd()
- if err != nil {
- return err
- }
- q.putRing()
- i.rxQueues = append(i.rxQueues, *q)
-
- for j := 0; j < q.ring.size; j++ {
- slot = qid*q.ring.size + j
- desc.setOffset(int(i.regions[0].packetBufferOffset + uint32(slot)*i.run.PacketBufferSize))
- q.putDescBuf(slot, desc)
- }
- }
-
- return nil
-}
-
-// connect finalizes interface connection
-func (i *Interface) connect() (err error) {
- for rid, _ := range i.regions {
- r := &i.regions[rid]
- if r.data == nil {
- r.data, err = syscall.Mmap(r.fd, 0, int(r.size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
- if err != nil {
- return fmt.Errorf("Mmap: %s", err)
- }
- }
- }
-
- for _, q := range i.txQueues {
- q.updateRing()
-
- if q.ring.getCookie() != cookie {
- return fmt.Errorf("Wrong cookie")
- }
-
- q.lastHead = 0
- q.lastTail = 0
- }
-
- for _, q := range i.rxQueues {
- q.updateRing()
-
- if q.ring.getCookie() != cookie {
- return fmt.Errorf("Wrong cookie")
- }
-
- q.lastHead = 0
- q.lastTail = 0
- }
-
- return i.args.ConnectedFunc(i)
-}
diff --git a/extras/gomemif/memif/interface_unsafe.go b/extras/gomemif/memif/interface_unsafe.go
deleted file mode 100644
index f5cbc2ed207..00000000000
--- a/extras/gomemif/memif/interface_unsafe.go
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-import (
- "fmt"
- "os"
- "syscall"
- "unsafe"
-)
-
-// memfdCreate returns memory file file descriptor (memif.sys_memfd_create)
-func memfdCreate() (mfd int, err error) {
- p0, err := syscall.BytePtrFromString("memif_region_0")
- if err != nil {
- return -1, fmt.Errorf("memfdCreate: %s", err)
- }
-
- u_mfd, _, errno := syscall.Syscall(sys_memfd_create, uintptr(unsafe.Pointer(p0)), uintptr(mfd_allow_sealing), uintptr(0))
- if errno != 0 {
- return -1, fmt.Errorf("memfdCreate: %s", os.NewSyscallError("memfd_create", errno))
- }
-
- return int(u_mfd), nil
-}
diff --git a/extras/gomemif/memif/memif.go b/extras/gomemif/memif/memif.go
deleted file mode 100644
index 1a7857de03e..00000000000
--- a/extras/gomemif/memif/memif.go
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-import (
- "encoding/binary"
- "fmt"
- "syscall"
-)
-
-const cookie = 0x3E31F20
-
-// VersionMajor is memif protocols major version
-const VersionMajor = 2
-
-// VersionMinor is memif protocols minor version
-const VersionMinor = 0
-
-// Version is memif protocols version as uint16
-// (M-Major m-minor: MMMMMMMMmmmmmmmm)
-const Version = ((VersionMajor << 8) | VersionMinor)
-
-type msgType uint16
-
-const (
- msgTypeNone msgType = iota
- msgTypeAck
- msgTypeHello
- msgTypeInit
- msgTypeAddRegion
- msgTypeAddRing
- msgTypeConnect
- msgTypeConnected
- msgTypeDisconnect
-)
-
-type interfaceMode uint8
-
-const (
- interfaceModeEthernet interfaceMode = iota
- interfaceModeIp
- interfaceModePuntInject
-)
-
-const msgSize = 128
-const msgTypeSize = 2
-
-const msgAddRingFlagS2M = (1 << 0)
-
-// Descriptor flags
-//
-// next buffer present
-const descFlagNext = (1 << 0)
-
-// Ring flags
-//
-// Interrupt
-const ringFlagInterrupt = 1
-
-func min16(a uint16, b uint16) uint16 {
- if a < b {
- return a
- }
- return b
-}
-
-func min8(a uint8, b uint8) uint8 {
- if a < b {
- return a
- }
- return b
-}
-
-type MsgHello struct {
- // app name
- Name [32]byte
- VersionMin uint16
- VersionMax uint16
- MaxRegion uint16
- MaxRingM2S uint16
- MaxRingS2M uint16
- MaxLog2RingSize uint8
-}
-
-type MsgInit struct {
- Version uint16
- Id uint32
- Mode interfaceMode
- Secret [24]byte
- // app name
- Name [32]byte
-}
-
-type MsgAddRegion struct {
- Index uint16
- Size uint64
-}
-
-type MsgAddRing struct {
- Flags uint16
- Index uint16
- Region uint16
- Offset uint32
- RingSizeLog2 uint8
- PrivateHdrSize uint16
-}
-
-type MsgConnect struct {
- // interface name
- Name [32]byte
-}
-
-type MsgConnected struct {
- // interface name
- Name [32]byte
-}
-
-type MsgDisconnect struct {
- Code uint32
- String [96]byte
-}
-
-/* DESCRIPTOR BEGIN */
-
-const descSize = 16
-
-// desc field offsets
-const descFlagsOffset = 0
-const descRegionOffset = 2
-const descLengthOffset = 4
-const descOffsetOffset = 8
-const descMetadataOffset = 12
-
-// descBuf represents a memif descriptor as array of bytes
-type descBuf []byte
-
-// newDescBuf returns new descriptor buffer
-func newDescBuf() descBuf {
- return make(descBuf, descSize)
-}
-
-// getDescBuff copies descriptor from shared memory to descBuf
-func (q *Queue) getDescBuf(slot int, db descBuf) {
- copy(db, q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize:])
-}
-
-// putDescBuf copies contents of descriptor buffer into shared memory
-func (q *Queue) putDescBuf(slot int, db descBuf) {
- copy(q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize:], db)
-}
-
-func (db descBuf) getFlags() int {
- return (int)(binary.LittleEndian.Uint16((db)[descFlagsOffset:]))
-}
-
-func (db descBuf) getRegion() int {
- return (int)(binary.LittleEndian.Uint16((db)[descRegionOffset:]))
-}
-
-func (db descBuf) getLength() int {
- return (int)(binary.LittleEndian.Uint32((db)[descLengthOffset:]))
-}
-
-func (db descBuf) getOffset() int {
- return (int)(binary.LittleEndian.Uint32((db)[descOffsetOffset:]))
-}
-
-func (db descBuf) getMetadata() int {
- return (int)(binary.LittleEndian.Uint32((db)[descMetadataOffset:]))
-}
-
-func (db descBuf) setFlags(val int) {
- binary.LittleEndian.PutUint16((db)[descFlagsOffset:], uint16(val))
-}
-
-func (db descBuf) setRegion(val int) {
- binary.LittleEndian.PutUint16((db)[descRegionOffset:], uint16(val))
-}
-
-func (db descBuf) setLength(val int) {
- binary.LittleEndian.PutUint32((db)[descLengthOffset:], uint32(val))
-}
-
-func (db descBuf) setOffset(val int) {
- binary.LittleEndian.PutUint32((db)[descOffsetOffset:], uint32(val))
-}
-
-func (db descBuf) setMetadata(val int) {
- binary.LittleEndian.PutUint32((db)[descMetadataOffset:], uint32(val))
-}
-
-/* DESCRIPTOR END */
-
-/* RING BEGIN */
-
-type ringType uint8
-
-const (
- ringTypeS2M ringType = iota
- ringTypeM2S
-)
-
-const ringSize = 128
-
-// ring field offsets
-const ringCookieOffset = 0
-const ringFlagsOffset = 4
-const ringHeadOffset = 6
-const ringTailOffset = 64
-
-// ringBuf represents a memif ring as array of bytes
-type ringBuf []byte
-
-type ring struct {
- ringType ringType
- size int
- log2Size int
- region int
- rb ringBuf
- offset int
-}
-
-// newRing returns new memif ring based on data received in msgAddRing (master only)
-func newRing(regionIndex int, ringType ringType, ringOffset int, log2RingSize int) *ring {
- r := &ring{
- ringType: ringType,
- size: (1 << log2RingSize),
- log2Size: log2RingSize,
- rb: make(ringBuf, ringSize),
- offset: ringOffset,
- }
-
- return r
-}
-
-// newRing returns a new memif ring
-func (i *Interface) newRing(regionIndex int, ringType ringType, ringIndex int) *ring {
- r := &ring{
- ringType: ringType,
- size: (1 << i.run.Log2RingSize),
- log2Size: int(i.run.Log2RingSize),
- rb: make(ringBuf, ringSize),
- }
-
- rSize := ringSize + descSize*r.size
- if r.ringType == ringTypeS2M {
- r.offset = 0
- } else {
- r.offset = int(i.run.NumQueuePairs) * rSize
- }
- r.offset += ringIndex * rSize
-
- return r
-}
-
-// putRing put the ring to the shared memory
-func (q *Queue) putRing() {
- copy(q.i.regions[q.ring.region].data[q.ring.offset:], q.ring.rb)
-}
-
-// updateRing updates ring with data from shared memory
-func (q *Queue) updateRing() {
- copy(q.ring.rb, q.i.regions[q.ring.region].data[q.ring.offset:])
-}
-
-func (r *ring) getCookie() int {
- return (int)(binary.LittleEndian.Uint32((r.rb)[ringCookieOffset:]))
-}
-
-// getFlags returns the flags value from ring buffer
-// Use Queue.getFlags in fast-path to avoid updating the whole ring.
-func (r *ring) getFlags() int {
- return (int)(binary.LittleEndian.Uint16((r.rb)[ringFlagsOffset:]))
-}
-
-// getHead returns the head pointer value from ring buffer.
-// Use readHead in fast-path to avoid updating the whole ring.
-func (r *ring) getHead() int {
- return (int)(binary.LittleEndian.Uint16((r.rb)[ringHeadOffset:]))
-}
-
-// getTail returns the tail pointer value from ring buffer.
-// Use readTail in fast-path to avoid updating the whole ring.
-func (r *ring) getTail() int {
- return (int)(binary.LittleEndian.Uint16((r.rb)[ringTailOffset:]))
-}
-
-func (r *ring) setCookie(val int) {
- binary.LittleEndian.PutUint32((r.rb)[ringCookieOffset:], uint32(val))
-}
-
-func (r *ring) setFlags(val int) {
- binary.LittleEndian.PutUint16((r.rb)[ringFlagsOffset:], uint16(val))
-}
-
-// setHead set the head pointer value int the ring buffer.
-// Use writeHead in fast-path to avoid putting the whole ring into shared memory.
-func (r *ring) setHead(val int) {
- binary.LittleEndian.PutUint16((r.rb)[ringHeadOffset:], uint16(val))
-}
-
-// setTail set the tail pointer value int the ring buffer.
-// Use writeTail in fast-path to avoid putting the whole ring into shared memory.
-func (r *ring) setTail(val int) {
- binary.LittleEndian.PutUint16((r.rb)[ringTailOffset:], uint16(val))
-}
-
-/* RING END */
-
-// isInterrupt returns true if the queue is in interrupt mode
-func (q *Queue) isInterrupt() bool {
- return (q.getFlags() & ringFlagInterrupt) == 0
-}
-
-// interrupt performs an interrupt if the queue is in interrupt mode
-func (q *Queue) interrupt() error {
- if q.isInterrupt() {
- buf := make([]byte, 8)
- binary.PutUvarint(buf, 1)
- n, err := syscall.Write(q.interruptFd, buf[:])
- if err != nil {
- return err
- }
- if n != 8 {
- return fmt.Errorf("Faild to write to eventfd")
- }
- }
-
- return nil
-}
diff --git a/extras/gomemif/memif/memif_unsafe.go b/extras/gomemif/memif/memif_unsafe.go
deleted file mode 100644
index 4469d26e982..00000000000
--- a/extras/gomemif/memif/memif_unsafe.go
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-import (
- "unsafe"
-)
-
-// readHead reads ring head directly form the shared memory
-func (q *Queue) readHead() (head int) {
- return (int)(*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringHeadOffset])))
- // return atomicload16(&q.i.regions[q.region].data[q.offset + descHeadOffset])
-}
-
-// readTail reads ring tail directly form the shared memory
-func (q *Queue) readTail() (tail int) {
- return (int)(*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringTailOffset])))
- // return atomicload16(&q.i.regions[q.region].data[q.offset + descTailOffset])
-}
-
-// writeHead writes ring head directly to the shared memory
-func (q *Queue) writeHead(value int) {
- *(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringHeadOffset])) = *(*uint16)(unsafe.Pointer(&value))
- //atomicstore16(&q.i.regions[q.region].data[q.offset + descHeadOffset], value)
-}
-
-// writeTail writes ring tail directly to the shared memory
-func (q *Queue) writeTail(value int) {
- *(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringTailOffset])) = *(*uint16)(unsafe.Pointer(&value))
- //atomicstore16(&q.i.regions[q.region].data[q.offset + descTailOffset], value)
-}
-
-func (q *Queue) setDescLength(slot int, length int) {
- *(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize+descLengthOffset])) = *(*uint16)(unsafe.Pointer(&length))
-}
-
-// getFlags reads ring flags directly from the shared memory
-func (q *Queue) getFlags() int {
- return (int)(*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringFlagsOffset])))
-}
diff --git a/extras/gomemif/memif/packet_reader.go b/extras/gomemif/memif/packet_reader.go
deleted file mode 100644
index 58338f6f2ab..00000000000
--- a/extras/gomemif/memif/packet_reader.go
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-import "fmt"
-
-// ReadPacket reads one packet form the shared memory and
-// returns the number of bytes read
-func (q *Queue) ReadPacket(pkt []byte) (int, error) {
- var mask int = q.ring.size - 1
- var slot int
- var lastSlot int
- var length int
- var offset int
- var pktOffset int = 0
- var nSlots uint16
- var desc descBuf = newDescBuf()
-
- if q.i.args.IsMaster {
- slot = int(q.lastHead)
- lastSlot = q.readHead()
- } else {
- slot = int(q.lastTail)
- lastSlot = q.readTail()
- }
-
- nSlots = uint16(lastSlot - slot)
- if nSlots == 0 {
- goto refill
- }
-
- // copy descriptor from shm
- q.getDescBuf(slot&mask, desc)
- length = desc.getLength()
- offset = desc.getOffset()
-
- copy(pkt[:], q.i.regions[desc.getRegion()].data[offset:offset+length])
- pktOffset += length
-
- slot++
- nSlots--
-
- for (desc.getFlags() & descFlagNext) == descFlagNext {
- if nSlots == 0 {
- return 0, fmt.Errorf("Incomplete chained buffer, may suggest peer error.")
- }
-
- q.getDescBuf(slot&mask, desc)
- length = desc.getLength()
- offset = desc.getOffset()
-
- copy(pkt[pktOffset:], q.i.regions[desc.getRegion()].data[offset:offset+length])
- pktOffset += length
-
- slot++
- nSlots--
- }
-
-refill:
- if q.i.args.IsMaster {
- q.lastHead = uint16(slot)
- q.writeTail(slot)
- } else {
- q.lastTail = uint16(slot)
-
- head := q.readHead()
-
- for nSlots := uint16(q.ring.size - head + int(q.lastTail)); nSlots > 0; nSlots-- {
- q.setDescLength(head&mask, int(q.i.run.PacketBufferSize))
- head++
- }
- q.writeHead(head)
- }
-
- return pktOffset, nil
-}
diff --git a/extras/gomemif/memif/packet_writer.go b/extras/gomemif/memif/packet_writer.go
deleted file mode 100644
index 702044f4b49..00000000000
--- a/extras/gomemif/memif/packet_writer.go
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2020 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.
- *------------------------------------------------------------------
- */
-
-package memif
-
-// WritePacket writes one packet to the shared memory and
-// returns the number of bytes written
-func (q *Queue) WritePacket(pkt []byte) int {
- var mask int = q.ring.size - 1
- var slot int
- var nFree uint16
- var packetBufferSize int = int(q.i.run.PacketBufferSize)
-
- if q.i.args.IsMaster {
- slot = q.readTail()
- nFree = uint16(q.readHead() - slot)
- } else {
- slot = q.readHead()
- nFree = uint16(q.ring.size - slot + q.readTail())
- }
-
- if nFree == 0 {
- q.interrupt()
- return 0
- }
-
- // copy descriptor from shm
- desc := newDescBuf()
- q.getDescBuf(slot&mask, desc)
- // reset flags
- desc.setFlags(0)
- // reset length
- if q.i.args.IsMaster {
- packetBufferSize = desc.getLength()
- }
- desc.setLength(0)
- offset := desc.getOffset()
-
- // write packet into memif buffer
- n := copy(q.i.regions[desc.getRegion()].data[offset:offset+packetBufferSize], pkt[:])
- desc.setLength(n)
- for n < len(pkt) {
- nFree--
- if nFree == 0 {
- q.interrupt()
- return 0
- }
- desc.setFlags(descFlagNext)
- q.putDescBuf(slot&mask, desc)
- slot++
-
- // copy descriptor from shm
- q.getDescBuf(slot&mask, desc)
- // reset flags
- desc.setFlags(0)
- // reset length
- if q.i.args.IsMaster {
- packetBufferSize = desc.getLength()
- }
- desc.setLength(0)
- offset := desc.getOffset()
-
- tmp := copy(q.i.regions[desc.getRegion()].data[offset:offset+packetBufferSize], pkt[:])
- desc.setLength(tmp)
- n += tmp
- }
-
- // copy descriptor to shm
- q.putDescBuf(slot&mask, desc)
- slot++
-
- if q.i.args.IsMaster {
- q.writeTail(slot)
- } else {
- q.writeHead(slot)
- }
-
- q.interrupt()
-
- return n
-}
diff --git a/extras/gomemif/migrated.txt b/extras/gomemif/migrated.txt
new file mode 100644
index 00000000000..abe913ad130
--- /dev/null
+++ b/extras/gomemif/migrated.txt
@@ -0,0 +1 @@
+Gomemif has been migrated to GoVPP repository https://github.com/FDio/govpp \ No newline at end of file
diff --git a/extras/hs-test/Makefile b/extras/hs-test/Makefile
new file mode 100644
index 00000000000..e247bf44160
--- /dev/null
+++ b/extras/hs-test/Makefile
@@ -0,0 +1,151 @@
+export HS_ROOT=$(CURDIR)
+
+ifeq ($(VERBOSE),)
+VERBOSE=false
+endif
+
+ifeq ($(PERSIST),)
+PERSIST=false
+endif
+
+ifeq ($(UNCONFIGURE),)
+UNCONFIGURE=false
+endif
+
+ifeq ($(TEST),)
+TEST=all
+endif
+
+ifeq ($(DEBUG),)
+DEBUG=false
+endif
+
+ifeq ($(CPUS),)
+CPUS=1
+endif
+
+ifeq ($(PARALLEL),)
+PARALLEL=1
+endif
+
+ifeq ($(REPEAT),)
+REPEAT=0
+endif
+
+ifeq ($(VPPSRC),)
+VPPSRC=$(shell pwd)/../..
+endif
+
+ifeq ($(UBUNTU_CODENAME),)
+UBUNTU_CODENAME=$(shell grep '^UBUNTU_CODENAME=' /etc/os-release | cut -f2- -d=)
+endif
+
+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
+ @echo "make build arguments:"
+ @echo " UBUNTU_VERSION - ubuntu version for docker image"
+ @echo " HST_EXTENDED_TESTS - build extended tests"
+ @echo
+ @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 " 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
+ @echo "List of all tests:"
+ $(call list_tests)
+
+.PHONY: list-tests
+list-tests:
+ $(call list_tests)
+
+.PHONY: build-vpp-release
+build-vpp-release:
+ @make -C ../.. build-release
+
+.PHONY: build-vpp-debug
+build-vpp-debug:
+ @make -C ../.. build
+
+.build.ok: build
+ @touch .build.ok
+
+.build_debug.ok: build-debug
+ @touch .build.ok
+
+.PHONY: test
+test: .deps.ok .build.ok
+ # '-' ignores the exit status, it is set in compress.sh
+ # necessary so gmake won't skip executing the bash script
+ -bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
+ --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
+ --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT)
+ @bash ./script/compress.sh
+
+.PHONY: test-debug
+test-debug: .deps.ok .build_debug.ok
+ # '-' ignores the exit status, it is set in compress.sh
+ # necessary so gmake won't skip executing the bash script
+ -bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
+ --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
+ --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT)
+ @bash ./script/compress.sh
+
+.PHONY: build-go
+build-go:
+ go build ./tools/http_server
+
+.PHONY: build
+build: .deps.ok build-vpp-release build-go
+ @rm -f .build.ok
+ bash ./script/build_hst.sh release
+ @touch .build.ok
+
+.PHONY: build-debug
+build-debug: .deps.ok build-vpp-debug build-go
+ @rm -f .build.ok
+ bash ./script/build_hst.sh debug
+ @touch .build.ok
+
+.deps.ok:
+ @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
+ @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
+ @touch .deps.ok
+
+.PHONY: fixstyle
+fixstyle:
+ @gofmt -w .
+ @go mod tidy
diff --git a/extras/hs-test/README.rst b/extras/hs-test/README.rst
new file mode 100644
index 00000000000..1dc1039b33f
--- /dev/null
+++ b/extras/hs-test/README.rst
@@ -0,0 +1,319 @@
+Host stack test framework
+=========================
+
+Overview
+--------
+
+The goal of the Host stack test framework (**hs-test**) is to ease writing and running end-to-end tests for VPP's Host Stack.
+End-to-end tests often want multiple VPP instances, network namespaces, different types of interfaces
+and to execute external tools or commands. With such requirements the existing VPP test framework is not sufficient.
+For this, ``Go`` was chosen as a high level language, allowing rapid development, with ``Docker`` and ``ip`` being the tools for creating required topology.
+
+`Ginkgo`_ forms the base framework upon which the *hs-test* is built and run.
+All tests are technically in a single suite because we are only using ``package main``. We simulate suite behavior by grouping tests by the topology they require.
+This allows us to run those mentioned groups in parallel, but not individual tests in parallel.
+
+
+Anatomy of a test case
+----------------------
+
+**Prerequisites**:
+
+* Install hs-test dependencies with ``make install-deps``
+* Tests use *hs-test*'s own docker image, so building it before starting tests is a prerequisite. Run ``make build[-debug]`` to do so
+* Docker has to be installed and Go has to be in path of both the running user and root
+* Root privileges are required to run tests as it uses Linux ``ip`` command for configuring topology
+
+**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),
+ 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
+#. 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])
+ #. 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
+ and clean-up of test topology
+
+Adding a test case
+------------------
+
+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``)
+#. Implement test behaviour inside the test method. This typically includes the following:
+
+ #. 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
+ #. 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.
+
+#. Create an ``init()`` function and register the test using ``register*SuiteTests(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``.
+
+::
+
+ package main
+
+ import (
+ "fmt"
+ )
+
+ func init(){
+ registerMySuiteTest(MyTest)
+ }
+
+ func MyTest(s *MySuite) {
+ clientVpp := s.getContainerByName("client-vpp").vppInstance
+
+ serverVethAddress := s.netInterfaces["server-iface"].AddressString()
+
+ result := clientVpp.vppctl("ping " + serverVethAddress)
+ s.assertNotNil(result)
+ s.log(result)
+ }
+
+Modifying the framework
+-----------------------
+
+**Adding a test suite**
+
+.. _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
+
+#. 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
+
+ ::
+
+ 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:
+
+ ::
+
+ func registerMySuiteTests(tests ...func(s *MySuite)) {
+ nginxTests = append(myTests, 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,
+ 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
+ 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
+
+ ::
+
+ func (s *MySuite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+
+ // Add custom setup code here
+
+ s.configureNetworkTopology("myTopology")
+ s.loadContainerTopology("2peerVeth")
+ }
+
+#. In suite file, implement ``SetupTest`` method which gets executed before each test. Starting containers and
+ configuring VPP is usually placed here
+
+ ::
+
+ func (s *MySuite) SetupTest() {
+ s.HstSuite.setupTest()
+ s.SetupVolumes()
+ s.SetupContainers()
+ }
+
+#. In order for ``Ginkgo`` to run this suite, we need to create a ``Describe`` container node with setup nodes and an ``It`` subject node.
+ Place them at the end of the suite file
+
+ * Declare a suite struct variable before anything else
+ * To use ``BeforeAll()`` and ``AfterAll()``, the container has to be marked as ``Ordered``
+ * Because the container is now marked as Ordered, if a test fails, all the subsequent tests are skipped.
+ To override this behavior, decorate the container node with ``ContinueOnFailure``
+
+ ::
+
+ 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()
+ })
+ 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))
+ }
+ })
+
+#. 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``).
+
+#. To run certain tests solo, create a new slice that will only contain tests that have to run solo and a new register function.
+ 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)
+ }, SpecTimeout(time.Minute*5))
+ })
+
+#. Next step is to add test cases to the suite. For that, see section `Adding a test case`_ above
+
+**Adding a topology element**
+
+Topology configuration exists as ``yaml`` files in the ``extras/hs-test/topo-network`` and
+``extras/hs-test/topo-containers`` folders. Processing of a network topology file for a particular test suite
+is started by the ``configureNetworkTopology`` method depending on which file's name is passed to it.
+Specified file is loaded and converted into internal data structures which represent various elements of the topology.
+After parsing the configuration, framework loops over the elements and configures them one by one on the host system.
+
+These are currently supported types of network elements.
+
+* ``netns`` - network namespace
+* ``veth`` - veth network interface, optionally with target network namespace or IPv4 address
+* ``bridge`` - ethernet bridge to connect created interfaces, optionally with target network namespace
+* ``tap`` - tap network interface with IP address
+
+Similarly, container topology is started by ``loadContainerTopology()``, configuration file is processed
+so that test suite retains map of defined containers and uses that to start them at the beginning
+of each test case and stop containers after the test finishes. Container configuration can specify
+also volumes which allow to share data between containers or between host system and containers.
+
+Supporting a new type of topology element requires adding code to recognize the new element type during loading.
+And adding code to set up the element in the host system with some Linux tool, such as *ip*.
+This should be implemented in ``netconfig.go`` for network and in ``container.go`` for containers and volumes.
+
+**Communicating between containers**
+
+When two VPP instances or other applications, each in its own Docker container,
+want to communicate there are typically two ways this can be done within *hs-test*.
+
+* Network interfaces. Containers are being created with ``-d --network host`` options,
+ so they are connected with interfaces created in host system
+* Shared folders. Containers are being created with ``-v`` option to create shared `volumes`_ between host system and containers
+ or just between containers
+
+Host system connects to VPP instances running in containers using a shared folder
+where binary API socket is accessible by both sides.
+
+**Adding an external tool**
+
+If an external program should be executed as part of a test case, it might be useful to wrap its execution in its own function.
+These types of functions are placed in the ``utils.go`` file. If the external program is not available by default in Docker image,
+add its installation to ``extras/hs-test/Dockerfile.vpp`` in ``apt-get install`` command.
+Alternatively copy the executable from host system to the Docker image, similarly how the VPP executables and libraries are being copied.
+
+**Skipping tests**
+
+``HstSuite`` provides several methods that can be called in tests for skipping it conditionally or unconditionally such as:
+``skip()``, ``SkipIfMultiWorker()``, ``SkipUnlessExtendedTestsBuilt()``. You can also use Ginkgo's ``Skip()``.
+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**
+
+It is possible to debug VPP by attaching ``gdb`` before test execution by adding ``DEBUG=true`` like follows:
+
+::
+
+ $ make test TEST=LDPreloadIperfVppTest DEBUG=true
+ ...
+ run following command in different terminal:
+ docker exec -it server-vpp2456109 gdb -ex "attach $(docker exec server-vpp2456109 pidof vpp)"
+ Afterwards press CTRL+\ to continue
+
+If a test consists of more VPP instances then this is done for each of them.
+
+
+**Eternal 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.
+
+
+.. _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/address_allocator.go b/extras/hs-test/address_allocator.go
new file mode 100644
index 00000000000..e05ea76b9bb
--- /dev/null
+++ b/extras/hs-test/address_allocator.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/edwarnicke/exechelper"
+)
+
+type AddressCounter = int
+
+type Ip4AddressAllocator struct {
+ networks map[int]AddressCounter
+ chosenOctet int
+ assignedIps []string
+}
+
+func (a *Ip4AddressAllocator) AddNetwork(networkNumber int) {
+ a.networks[networkNumber] = 1
+}
+
+func (a *Ip4AddressAllocator) NewIp4InterfaceAddress(inputNetworkNumber ...int) (string, error) {
+ var networkNumber int = 0
+ if len(inputNetworkNumber) > 0 {
+ networkNumber = inputNetworkNumber[0]
+ }
+
+ if _, ok := a.networks[networkNumber]; !ok {
+ a.AddNetwork(networkNumber)
+ }
+
+ numberOfAddresses := a.networks[networkNumber]
+
+ if numberOfAddresses == 254 {
+ return "", fmt.Errorf("no available IPv4 addresses")
+ }
+
+ address, err := a.createIpAddress(networkNumber, numberOfAddresses)
+
+ a.networks[networkNumber] = numberOfAddresses + 1
+
+ return address + "/24", err
+}
+
+// Creates a file every time an IP is assigned: used to keep track of addresses in use.
+// If an address is not in use, 'counter' is then copied to 'chosenOctet' and it is used for the remaining tests.
+// Also checks host IP addresses.
+func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddresses int) (string, error) {
+ hostIps, _ := exechelper.CombinedOutput("ip a")
+ counter := 10
+ var address string
+
+ for {
+ if a.chosenOctet != 0 {
+ address = fmt.Sprintf("10.%v.%v.%v", a.chosenOctet, networkNumber, numberOfAddresses)
+ file, err := os.Create(address)
+ if err != nil {
+ return "", errors.New("unable to create file: " + fmt.Sprint(err))
+ }
+ file.Close()
+ break
+ } else {
+ address = fmt.Sprintf("10.%v.%v.%v", counter, networkNumber, numberOfAddresses)
+ _, err := os.Stat(address)
+ if err == nil || strings.Contains(string(hostIps), address) {
+ counter++
+ } else if os.IsNotExist(err) {
+ file, err := os.Create(address)
+ if err != nil {
+ return "", errors.New("unable to create file: " + fmt.Sprint(err))
+ }
+ file.Close()
+ a.chosenOctet = counter
+ break
+ } else {
+ return "", errors.New("an error occurred while checking if a file exists: " + fmt.Sprint(err))
+ }
+ }
+ }
+
+ a.assignedIps = append(a.assignedIps, address)
+ return address, nil
+}
+
+func (a *Ip4AddressAllocator) deleteIpAddresses() {
+ for ip := range a.assignedIps {
+ os.Remove(a.assignedIps[ip])
+ }
+}
+
+func NewIp4AddressAllocator() *Ip4AddressAllocator {
+ var ip4AddrAllocator = new(Ip4AddressAllocator)
+ ip4AddrAllocator.networks = make(map[int]AddressCounter)
+ ip4AddrAllocator.AddNetwork(0)
+ return ip4AddrAllocator
+}
diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go
new file mode 100644
index 00000000000..3ac5eee3aaa
--- /dev/null
+++ b/extras/hs-test/container.go
@@ -0,0 +1,373 @@
+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.apiStream != 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
new file mode 100644
index 00000000000..a976f47d8a5
--- /dev/null
+++ b/extras/hs-test/cpu.go
@@ -0,0 +1,90 @@
+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/docker/Dockerfile.build b/extras/hs-test/docker/Dockerfile.build
new file mode 100644
index 00000000000..8b2652e93fc
--- /dev/null
+++ b/extras/hs-test/docker/Dockerfile.build
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 00000000000..81d15e86c82
--- /dev/null
+++ b/extras/hs-test/docker/Dockerfile.curl
@@ -0,0 +1,7 @@
+FROM hs-test/build
+
+COPY script/build_curl.sh /build_curl.sh
+RUN apt-get update && apt-get install wget
+RUN /build_curl.sh
+
+CMD ["/bin/sh"]
diff --git a/extras/hs-test/docker/Dockerfile.nginx b/extras/hs-test/docker/Dockerfile.nginx
new file mode 100644
index 00000000000..11ec6af156d
--- /dev/null
+++ b/extras/hs-test/docker/Dockerfile.nginx
@@ -0,0 +1,20 @@
+ARG UBUNTU_VERSION
+
+FROM ubuntu:${UBUNTU_VERSION}
+
+RUN apt-get update \
+ && apt-get install -y nginx gdb less \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY vpp-data/lib/* /usr/lib/
+COPY resources/nginx/vcl.conf /vcl.conf
+COPY resources/nginx/nginx.conf /nginx.conf
+COPY script/nginx_ldp.sh /usr/bin/nginx_ldp.sh
+
+ENV VCL_CONFIG=/vcl.conf
+ENV LDP=/usr/lib/libvcl_ldpreload.so
+ENV LDP_DEBUG=0
+ENV VCL_DEBUG=0
+ENV LDP_SID_BIT=8
+
+ENTRYPOINT ["nginx_ldp.sh", "nginx", "-c", "/nginx.conf"]
diff --git a/extras/hs-test/docker/Dockerfile.nginx-http3 b/extras/hs-test/docker/Dockerfile.nginx-http3
new file mode 100644
index 00000000000..5d66a2528a6
--- /dev/null
+++ b/extras/hs-test/docker/Dockerfile.nginx-http3
@@ -0,0 +1,24 @@
+FROM hs-test/build
+
+COPY script/build_boringssl.sh /build_boringssl.sh
+RUN git clone https://boringssl.googlesource.com/boringssl
+RUN ./build_boringssl.sh
+
+COPY script/build_nginx.sh /build_nginx.sh
+RUN git clone https://github.com/nginx/nginx
+RUN ./build_nginx.sh
+
+COPY vpp-data/lib/* /usr/lib/
+COPY resources/nginx/vcl.conf /vcl.conf
+COPY resources/nginx/nginx_http3.conf /nginx.conf
+COPY script/nginx_ldp.sh /usr/bin/nginx_ldp.sh
+
+COPY resources/nginx/html/index.html /usr/share/nginx/index.html
+
+ENV VCL_CONFIG=/vcl.conf
+ENV LDP=/usr/lib/libvcl_ldpreload.so
+ENV LDP_DEBUG=0
+ENV VCL_DEBUG=0
+ENV LDP_SID_BIT=8
+
+ENTRYPOINT ["nginx_ldp.sh", "/usr/local/nginx/sbin/nginx", "-c", "/nginx.conf"]
diff --git a/extras/hs-test/docker/Dockerfile.nginx-server b/extras/hs-test/docker/Dockerfile.nginx-server
new file mode 100644
index 00000000000..1971158131b
--- /dev/null
+++ b/extras/hs-test/docker/Dockerfile.nginx-server
@@ -0,0 +1,12 @@
+ARG UBUNTU_VERSION
+
+FROM ubuntu:${UBUNTU_VERSION}
+
+RUN apt-get update \
+ && apt-get install -y nginx \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY resources/nginx/nginx_server_mirroring.conf /nginx.conf
+
+
+ENTRYPOINT ["nginx", "-c", "/nginx.conf"]
diff --git a/extras/hs-test/docker/Dockerfile.vpp b/extras/hs-test/docker/Dockerfile.vpp
new file mode 100644
index 00000000000..ace83c593c6
--- /dev/null
+++ b/extras/hs-test/docker/Dockerfile.vpp
@@ -0,0 +1,33 @@
+ARG UBUNTU_VERSION
+
+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 \
+ && rm -rf /var/lib/apt/lists/*
+
+ENV DIR=vpp-data/lib/vpp_plugins
+COPY \
+ $DIR/af_packet_plugin.so \
+ $DIR/hs_apps_plugin.so \
+ $DIR/http_plugin.so \
+ $DIR/unittest_plugin.so \
+ $DIR/quic_plugin.so \
+ $DIR/http_static_plugin.so \
+ $DIR/ping_plugin.so \
+ $DIR/nsim_plugin.so \
+ $DIR/prom_plugin.so \
+ $DIR/tlsopenssl_plugin.so \
+ /usr/lib/x86_64-linux-gnu/vpp_plugins/
+
+COPY vpp-data/bin/vpp /usr/bin/
+COPY vpp-data/bin/vppctl /usr/bin/
+COPY vpp-data/bin/vpp_echo /usr/bin/
+COPY vpp-data/bin/vcl_* /usr/bin/
+COPY vpp-data/lib/*.so /usr/lib/
+
+RUN addgroup vpp
+
+ENTRYPOINT ["tail", "-f", "/dev/null"]
diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go
new file mode 100644
index 00000000000..33728db6c97
--- /dev/null
+++ b/extras/hs-test/echo_test.go
@@ -0,0 +1,49 @@
+package main
+
+func init() {
+ registerVethTests(EchoBuiltinTest)
+ registerSoloVethTests(TcpWithLossTest)
+}
+
+func EchoBuiltinTest(s *VethsSuite) {
+ serverVpp := s.getContainerByName("server-vpp").vppInstance
+ serverVeth := s.getInterfaceByName(serverInterfaceName)
+
+ serverVpp.vppctl("test echo server " +
+ " uri tcp://" + serverVeth.ip4AddressString() + "/1234")
+
+ clientVpp := s.getContainerByName("client-vpp").vppInstance
+
+ 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:")
+}
+
+func TcpWithLossTest(s *VethsSuite) {
+ serverVpp := s.getContainerByName("server-vpp").vppInstance
+
+ serverVeth := s.getInterfaceByName(serverInterfaceName)
+ serverVpp.vppctl("test echo server uri tcp://%s/20022",
+ serverVeth.ip4AddressString())
+
+ 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())
+
+ // Add loss of packets with Network Delay Simulator
+ 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)
+
+ // 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", output)
+}
diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go
new file mode 100644
index 00000000000..8773fa2417d
--- /dev/null
+++ b/extras/hs-test/framework_test.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestHst(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "HST")
+}
diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod
new file mode 100644
index 00000000000..3be9ba20a86
--- /dev/null
+++ b/extras/hs-test/go.mod
@@ -0,0 +1,33 @@
+module fd.io/hs-test
+
+go 1.21
+
+require (
+ github.com/edwarnicke/exechelper v1.0.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/fsnotify/fsnotify v1.7.0 // 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/pkg/errors v0.9.1 // indirect
+ github.com/sirupsen/logrus v1.9.3
+ github.com/vishvananda/netns v0.0.4 // indirect
+ golang.org/x/sys v0.16.0 // indirect
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
+)
diff --git a/extras/hs-test/go.sum b/extras/hs-test/go.sum
new file mode 100644
index 00000000000..479b0289814
--- /dev/null
+++ b/extras/hs-test/go.sum
@@ -0,0 +1,70 @@
+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/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/edwarnicke/exechelper v1.0.3 h1:OY2ocGAITTqnEDvZk0dRQSeMIQvyH0SyL/4ncz+5GeQ=
+github.com/edwarnicke/exechelper v1.0.3/go.mod h1:R65OUPKns4bgeHkCmfSHbmqLBU8aHZxTgLmEyUBUk4U=
+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/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/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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+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/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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+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/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=
+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=
+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-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=
+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/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=
diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go
new file mode 100644
index 00000000000..35553f00ff3
--- /dev/null
+++ b/extras/hs-test/hst_suite.go
@@ -0,0 +1,503 @@
+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 {
+ 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
+}
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
new file mode 100644
index 00000000000..37416194e6c
--- /dev/null
+++ b/extras/hs-test/http_test.go
@@ -0,0 +1,285 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "strings"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+func init() {
+ registerNsTests(HttpTpsTest)
+ registerVethTests(HttpCliTest, HttpCliConnectErrorTest)
+ registerNoTopoTests(NginxHttp3Test, NginxAsServerTest,
+ NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest, HeaderServerTest,
+ HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest,
+ HttpCliBadRequestTest)
+ registerNoTopoSoloTests(HttpStaticPromTest)
+}
+
+func HttpTpsTest(s *NsSuite) {
+ iface := s.getInterfaceByName(clientInterface)
+ client_ip := iface.ip4AddressString()
+ port := "8080"
+ finished := make(chan error, 1)
+ clientNetns := s.getNetNamespaceByName("cln")
+
+ container := s.getContainerByName("vpp")
+
+ // configure vpp in the container
+ container.vppInstance.vppctl("http tps uri tcp://0.0.0.0/8080")
+
+ 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 HttpCliTest(s *VethsSuite) {
+ serverContainer := s.getContainerByName("server-vpp")
+ clientContainer := s.getContainerByName("client-vpp")
+
+ serverVeth := s.getInterfaceByName(serverInterfaceName)
+
+ serverContainer.vppInstance.vppctl("http cli server")
+
+ uri := "http://" + serverVeth.ip4AddressString() + "/80"
+
+ 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!")
+}
+
+func HttpCliConnectErrorTest(s *VethsSuite) {
+ clientContainer := s.getContainerByName("client-vpp")
+
+ serverVeth := s.getInterfaceByName(serverInterfaceName)
+
+ uri := "http://" + serverVeth.ip4AddressString() + "/80"
+
+ o := clientContainer.vppInstance.vppctl("http cli client" +
+ " uri " + uri + " query /show/vlib/graph")
+
+ s.log(o)
+ s.assertContains(o, "failed to connect")
+}
+
+func NginxHttp3Test(s *NoTopoSuite) {
+ s.SkipUnlessExtendedTestsBuilt()
+
+ query := "index.html"
+ nginxCont := s.getContainerByName("nginx-http3")
+ s.assertNil(nginxCont.run())
+
+ vpp := s.getContainerByName("vpp").vppInstance
+ vpp.waitForApp("nginx-", 5)
+ serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+
+ 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!")
+}
+
+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"))
+ time.Sleep(time.Second * 5)
+ go func() {
+ defer GinkgoRecover()
+ s.startWget(finished, serverAddress, "80", query, "")
+ }()
+ err := <-finished
+ s.assertNil(err)
+}
+
+func HttpStaticMovedTest(s *NoTopoSuite) {
+ vpp := s.getContainerByName("vpp").vppInstance
+ vpp.container.exec("mkdir -p /tmp/tmp.aaa")
+ vpp.container.createFile("/tmp/tmp.aaa/index.html", "<http><body><p>Hello</p></body></http>")
+ serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+ s.log(vpp.vppctl("http static server www-root /tmp uri tcp://" + serverAddress + "/80 debug"))
+
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ client := &http.Client{
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ },
+ Transport: transport,
+ }
+ 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.assertEqual(301, resp.StatusCode)
+ s.assertNotEqual("", resp.Header.Get("Location"))
+}
+
+func HttpStaticNotFoundTest(s *NoTopoSuite) {
+ vpp := s.getContainerByName("vpp").vppInstance
+ serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+ s.log(vpp.vppctl("http static server www-root /tmp uri tcp://" + serverAddress + "/80 debug"))
+
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ client := &http.Client{Transport: transport}
+ 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.assertEqual(404, resp.StatusCode)
+}
+
+func HttpCliMethodNotAllowedTest(s *NoTopoSuite) {
+ vpp := s.getContainerByName("vpp").vppInstance
+ serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+ vpp.vppctl("http cli server")
+
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ client := &http.Client{Transport: transport}
+ 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.assertEqual(405, resp.StatusCode)
+ // TODO: need to be fixed in http code
+ //s.assertNotEqual("", resp.Header.Get("Allow"))
+}
+
+func HttpCliBadRequestTest(s *NoTopoSuite) {
+ vpp := s.getContainerByName("vpp").vppInstance
+ serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+ vpp.vppctl("http cli server")
+
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ client := &http.Client{Transport: transport}
+ 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.assertEqual(400, resp.StatusCode)
+}
+
+func HeaderServerTest(s *NoTopoSuite) {
+ vpp := s.getContainerByName("vpp").vppInstance
+ serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString()
+ vpp.vppctl("http cli server")
+
+ transport := http.DefaultTransport
+ transport.(*http.Transport).Proxy = nil
+ client := &http.Client{Transport: transport}
+ 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.assertEqual("http_cli_server", resp.Header.Get("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
+ }
+ }
+ 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)
+ } 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)
+ }
+ return nil
+}
+
+func NginxPerfCpsTest(s *NoTopoSuite) {
+ s.assertNil(runNginxPerf(s, "cps", "ab"))
+}
+
+func NginxPerfRpsTest(s *NoTopoSuite) {
+ s.assertNil(runNginxPerf(s, "rps", "ab"))
+}
+
+func NginxPerfWrkTest(s *NoTopoSuite) {
+ s.assertNil(runNginxPerf(s, "", "wrk"))
+}
diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go
new file mode 100644
index 00000000000..24d2de39485
--- /dev/null
+++ b/extras/hs-test/ldp_test.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+func init() {
+ registerVethTests(LDPreloadIperfVppTest)
+}
+
+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"
+
+ ldpreload := "LD_PRELOAD=../../build-root/build-vpp-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so"
+
+ stopServerCh := make(chan struct{}, 1)
+ srvCh := make(chan error, 1)
+ clnCh := make(chan error)
+
+ 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)
+ }()
+
+ 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()
+ go func() {
+ defer GinkgoRecover()
+ s.startClientApp(serverVethAddress, clnEnv, clnCh, clnRes)
+ }()
+ s.log(<-clnRes)
+
+ // wait for client's result
+ err = <-clnCh
+ s.assertNil(err, fmt.Sprint(err))
+
+ // stop server
+ stopServerCh <- struct{}{}
+}
diff --git a/extras/hs-test/linux_iperf_test.go b/extras/hs-test/linux_iperf_test.go
new file mode 100644
index 00000000000..e323f7fb721
--- /dev/null
+++ b/extras/hs-test/linux_iperf_test.go
@@ -0,0 +1,40 @@
+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/mirroring_test.go b/extras/hs-test/mirroring_test.go
new file mode 100644
index 00000000000..6c5a860b01c
--- /dev/null
+++ b/extras/hs-test/mirroring_test.go
@@ -0,0 +1,24 @@
+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/netconfig.go b/extras/hs-test/netconfig.go
new file mode 100644
index 00000000000..c76a0fda5f5
--- /dev/null
+++ b/extras/hs-test/netconfig.go
@@ -0,0 +1,383 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os/exec"
+ "strings"
+
+ "go.fd.io/govpp/binapi/ethernet_types"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip_types"
+)
+
+type (
+ Cmd = exec.Cmd
+ MacAddress = ethernet_types.MacAddress
+ AddressWithPrefix = ip_types.AddressWithPrefix
+ IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix
+ InterfaceIndex = interface_types.InterfaceIndex
+
+ NetConfig interface {
+ configure() error
+ unconfigure()
+ Name() string
+ Type() string
+ }
+
+ NetConfigBase struct {
+ name string
+ category string // what else to call this when `type` is reserved?
+ }
+
+ NetInterface struct {
+ NetConfigBase
+ ip4AddrAllocator *Ip4AddressAllocator
+ ip4Address string
+ index InterfaceIndex
+ hwAddress MacAddress
+ networkNamespace string
+ networkNumber int
+ peer *NetInterface
+ }
+
+ NetworkNamespace struct {
+ NetConfigBase
+ }
+
+ NetworkBridge struct {
+ NetConfigBase
+ networkNamespace string
+ interfaces []string
+ }
+)
+
+const (
+ NetNs string = "netns"
+ Veth string = "veth"
+ Tap string = "tap"
+ Bridge string = "bridge"
+)
+
+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)
+ },
+ Tap: func(n *NetInterface) *Cmd {
+ return exec.Command("ip", "tuntap", "add", n.name, "mode", "tap")
+ },
+ }
+)
+
+func newNetworkInterface(cfg NetDevConfig, a *Ip4AddressAllocator) (*NetInterface, error) {
+ var newInterface *NetInterface = &NetInterface{}
+ var err error
+ newInterface.ip4AddrAllocator = a
+ newInterface.name = cfg["name"].(string)
+ 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))
+ if err != nil {
+ return &NetInterface{}, err
+ }
+ }
+
+ if netns, ok := cfg["netns"]; ok {
+ newInterface.networkNamespace = netns.(string)
+ }
+
+ if ip, ok := cfg["ip4"]; ok {
+ if n, ok := ip.(NetDevConfig)["network"]; ok {
+ newInterface.networkNumber = n.(int)
+ }
+ newInterface.ip4Address, err = newInterface.ip4AddrAllocator.NewIp4InterfaceAddress(
+ newInterface.networkNumber,
+ )
+ if err != nil {
+ return &NetInterface{}, err
+ }
+ }
+
+ if _, ok := cfg["peer"]; !ok {
+ return newInterface, nil
+ }
+
+ peer := cfg["peer"].(NetDevConfig)
+
+ if newInterface.peer, err = newNetworkInterface(peer, a); err != nil {
+ return &NetInterface{}, err
+ }
+
+ return newInterface, nil
+}
+
+func (n *NetInterface) configureUpState() error {
+ err := setDevUp(n.Name(), "")
+ if err != nil {
+ return fmt.Errorf("set link up failed: %v", err)
+ }
+ return nil
+}
+
+func (n *NetInterface) configureNetworkNamespace() error {
+ if n.networkNamespace != "" {
+ err := linkSetNetns(n.name, n.networkNamespace)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (n *NetInterface) configureAddress() error {
+ if n.ip4Address != "" {
+ if err := addAddress(
+ n.Name(),
+ n.ip4Address,
+ n.networkNamespace,
+ ); err != nil {
+ return err
+ }
+
+ }
+ return nil
+}
+
+func (n *NetInterface) configure() error {
+ cmd := ipCommandMap[n.Type()](n)
+ _, err := cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("creating interface '%v' failed: %v", n.Name(), err)
+ }
+
+ if err := n.configureUpState(); err != nil {
+ return err
+ }
+
+ if err := n.configureNetworkNamespace(); err != nil {
+ return err
+ }
+
+ if err := n.configureAddress(); err != nil {
+ return err
+ }
+
+ if n.peer != nil && n.peer.name != "" {
+ if err := n.peer.configureUpState(); err != nil {
+ return err
+ }
+
+ if err := n.peer.configureNetworkNamespace(); err != nil {
+ return err
+ }
+
+ if err := n.peer.configureAddress(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (n *NetInterface) unconfigure() {
+ delLink(n.name)
+}
+
+func (n *NetInterface) Name() string {
+ return n.name
+}
+
+func (n *NetInterface) Type() string {
+ return n.category
+}
+
+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) ip4AddressString() string {
+ return strings.Split(n.ip4Address, "/")[0]
+}
+
+func (b *NetConfigBase) Name() string {
+ return b.name
+}
+
+func (b *NetConfigBase) Type() string {
+ return b.category
+}
+
+func newNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
+ var networkNamespace NetworkNamespace
+ networkNamespace.name = cfg["name"].(string)
+ networkNamespace.category = NetNs
+ return networkNamespace, nil
+}
+
+func (ns *NetworkNamespace) configure() error {
+ return addDelNetns(ns.name, true)
+}
+
+func (ns *NetworkNamespace) unconfigure() {
+ addDelNetns(ns.name, false)
+}
+
+func newBridge(cfg NetDevConfig) (NetworkBridge, error) {
+ var bridge NetworkBridge
+ bridge.name = cfg["name"].(string)
+ bridge.category = Bridge
+ for _, v := range cfg["interfaces"].([]interface{}) {
+ bridge.interfaces = append(bridge.interfaces, v.(string))
+ }
+
+ bridge.networkNamespace = ""
+ if netns, ok := cfg["netns"]; ok {
+ bridge.networkNamespace = netns.(string)
+ }
+ return bridge, nil
+}
+
+func (b *NetworkBridge) configure() error {
+ return addBridge(b.name, b.interfaces, b.networkNamespace)
+}
+
+func (b *NetworkBridge) unconfigure() {
+ delBridge(b.name, b.networkNamespace)
+}
+
+func delBridge(brName, ns string) error {
+ err := setDevDown(brName, ns)
+ if err != nil {
+ return err
+ }
+
+ err = addDelBridge(brName, ns, false)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func setDevUp(dev, ns string) error {
+ return setDevUpDown(dev, ns, true)
+}
+
+func setDevDown(dev, ns string) error {
+ return setDevUpDown(dev, ns, false)
+}
+
+func delLink(ifName string) {
+ cmd := exec.Command("ip", "link", "del", ifName)
+ cmd.Run()
+}
+
+func setDevUpDown(dev, ns string, isUp bool) error {
+ var op string
+ if isUp {
+ op = "up"
+ } else {
+ op = "down"
+ }
+ c := []string{"ip", "link", "set", "dev", dev, op}
+ cmd := appendNetns(c, ns)
+ err := cmd.Run()
+ if err != nil {
+ return fmt.Errorf("error bringing %s device %s! (cmd: '%s')", dev, op, cmd)
+ }
+ return nil
+}
+
+func addDelNetns(name string, isAdd bool) error {
+ var op string
+ if isAdd {
+ op = "add"
+ } else {
+ op = "del"
+ }
+ cmd := exec.Command("ip", "netns", op, name)
+ _, err := cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("add/del netns failed (cmd: '%s')", cmd)
+ }
+ return nil
+}
+
+func linkSetNetns(ifName, ns string) error {
+ cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns)
+ err := cmd.Run()
+ if err != nil {
+ return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err)
+ }
+ return nil
+}
+
+func newCommand(s []string, ns string) *exec.Cmd {
+ return appendNetns(s, ns)
+}
+
+func appendNetns(s []string, ns string) *exec.Cmd {
+ var cmd *exec.Cmd
+ if ns == "" {
+ // use default namespace
+ cmd = exec.Command(s[0], s[1:]...)
+ } else {
+ var args = []string{"netns", "exec", ns}
+ args = append(args, s[:]...)
+ cmd = exec.Command("ip", args...)
+ }
+ return cmd
+}
+
+func addDelBridge(brName, ns string, isAdd bool) error {
+ var op string
+ if isAdd {
+ op = "addbr"
+ } else {
+ op = "delbr"
+ }
+ var c = []string{"brctl", op, brName}
+ cmd := appendNetns(c, ns)
+ err := cmd.Run()
+ if err != nil {
+ s := fmt.Sprintf("%s %s failed! err: '%s'", op, brName, err)
+ return errors.New(s)
+ }
+ return nil
+}
+
+func addBridge(brName string, ifs []string, ns string) error {
+ err := addDelBridge(brName, ns, true)
+ if err != nil {
+ return err
+ }
+
+ for _, v := range ifs {
+ c := []string{"brctl", "addif", brName, v}
+ cmd := appendNetns(c, ns)
+ err = cmd.Run()
+ if err != nil {
+ return fmt.Errorf("error adding %s to bridge %s: %s", v, brName, err)
+ }
+ }
+ err = setDevUp(brName, ns)
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go
new file mode 100644
index 00000000000..ac5f94c8535
--- /dev/null
+++ b/extras/hs-test/proxy_test.go
@@ -0,0 +1,115 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/edwarnicke/exechelper"
+ . "github.com/onsi/ginkgo/v2"
+)
+
+func init() {
+ registerNsTests(VppProxyHttpTcpTest, VppProxyHttpTlsTest, EnvoyProxyHttpTcpTest)
+}
+
+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 *NsSuite, proto string) {
+ serverVeth := s.getInterfaceByName(serverInterface)
+ clientVeth := s.getInterfaceByName(clientInterface)
+
+ 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 VppProxyHttpTcpTest(s *NsSuite) {
+ proto := "tcp"
+ configureVppProxy(s, proto)
+ err := testProxyHttpTcp(s, proto)
+ s.assertNil(err, fmt.Sprint(err))
+}
+
+func VppProxyHttpTlsTest(s *NsSuite) {
+ proto := "tls"
+ configureVppProxy(s, proto)
+ err := testProxyHttpTcp(s, proto)
+ s.assertNil(err, fmt.Sprint(err))
+}
+
+func configureEnvoyProxy(s *NsSuite) {
+ envoyContainer := s.getContainerByName("envoy")
+ err := envoyContainer.create()
+ s.assertNil(err, "Error creating envoy container: %s", err)
+
+ 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 EnvoyProxyHttpTcpTest(s *NsSuite) {
+ configureEnvoyProxy(s)
+ err := testProxyHttpTcp(s, "tcp")
+ s.assertNil(err, fmt.Sprint(err))
+}
diff --git a/extras/hs-test/raw_session_test.go b/extras/hs-test/raw_session_test.go
new file mode 100644
index 00000000000..5c66df0b1ce
--- /dev/null
+++ b/extras/hs-test/raw_session_test.go
@@ -0,0 +1,40 @@
+package main
+
+func init() {
+ registerVethTests(VppEchoQuicTest, VppEchoTcpTest)
+}
+
+func VppEchoQuicTest(s *VethsSuite) {
+ s.testVppEcho("quic")
+}
+
+// TODO: udp echo currently broken in vpp
+func VppEchoUdpTest(s *VethsSuite) {
+ s.testVppEcho("udp")
+}
+
+func VppEchoTcpTest(s *VethsSuite) {
+ s.testVppEcho("tcp")
+}
+
+func (s *VethsSuite) testVppEcho(proto string) {
+ serverVethAddress := s.getInterfaceByName(serverInterfaceName).ip4AddressString()
+ uri := proto + "://" + serverVethAddress + "/12344"
+
+ echoSrvContainer := s.getContainerByName("server-app")
+ serverCommand := "vpp_echo server TX=RX" +
+ " socket-name " + echoSrvContainer.getContainerWorkDir() + "/var/run/app_ns_sockets/default" +
+ " use-app-socket-api" +
+ " uri " + uri
+ s.log(serverCommand)
+ echoSrvContainer.execServer(serverCommand)
+
+ echoClnContainer := s.getContainerByName("client-app")
+
+ clientCommand := "vpp_echo client" +
+ " 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)
+}
diff --git a/extras/hs-test/resources/cert/localhost.crt b/extras/hs-test/resources/cert/localhost.crt
new file mode 100644
index 00000000000..b21fb48906e
--- /dev/null
+++ b/extras/hs-test/resources/cert/localhost.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIUF116CAipHqQBCyAEvNesV0u4u0swDQYJKoZIhvcNAQEL
+BQAwQjELMAkGA1UEBhMCU0sxEDAOBgNVBAgMB1ZwcExhbmQxITAfBgNVBAoMGElu
+dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA1MjkxMDI0MjhaFw0yNDA1Mjgx
+MDI0MjhaMEIxCzAJBgNVBAYTAlNLMRAwDgYDVQQIDAdWcHBMYW5kMSEwHwYDVQQK
+DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCy40rDzrrHPGIyhP24hOBQefEgKD5uUGgSUyJTCur4yB/r2PGt
+LlfipKwDmNArmZuFOgKh8evipu2jYaxf4GHQmi7PGLddvPkqo5FWtVW8oAVJMcp+
+fwfs7OgkqtYD6Y7qjmjfXb9+rMpPN8WZ7cKbJwZpF3lf8GGaLqRmPiQg2j8qzcVy
+nz8cIwBZP8BJVclA9GIagijY7Zcmz0HnTPrPoLMeyLJOTqPMfkUYA2H2eHeISkQP
+BeoFoiwCI5eM35UiWiLyiv9Kojn4BHx6MLrfKBjV13WtcRMgYm5VftsWOZ92lmHm
+bpj9mGgtd84JWtWxs33oG4mNRSAeujf9AE5VAgMBAAGjUzBRMB0GA1UdDgQWBBTj
+s+A5M/Cao+0Phgg6xFBKIPxLqjAfBgNVHSMEGDAWgBTjs+A5M/Cao+0Phgg6xFBK
+IPxLqjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB3EcGDby5u
+cEGjgAFR18kH4ztnYUdZUrPI72sOFfjRLtJpx00n759SBawqNW1Y2a1QRd+GgUBK
+YpYd2gzWYFjf/4c5BN4SrjeZGnQ8N0YomqqGKvOQO0YdYK4i/lWJjLRaLiVBn9EX
+Z+odYhGqQgoAJHnm5Mmqhx9ts8qxZLbdsh+T93mKvj+/yuai2Is+AJfLgZpdKPQN
+bCoZemRm+nghRvEP8aX/469wiz7SOLqUzxrTOtXV48wTU5LWLDCs1lF9ZdGHR9/r
+vj8unnEHIZiH3ZjN7OgaAoNHZE26Ywbmllc/a0vPw8iHdrLe7+Wtp4zXe2rcxhW7
+b+X1/yRCZ+Wg
+-----END CERTIFICATE-----
diff --git a/extras/hs-test/resources/cert/localhost.key b/extras/hs-test/resources/cert/localhost.key
new file mode 100644
index 00000000000..2d65db50900
--- /dev/null
+++ b/extras/hs-test/resources/cert/localhost.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCy40rDzrrHPGIy
+hP24hOBQefEgKD5uUGgSUyJTCur4yB/r2PGtLlfipKwDmNArmZuFOgKh8evipu2j
+Yaxf4GHQmi7PGLddvPkqo5FWtVW8oAVJMcp+fwfs7OgkqtYD6Y7qjmjfXb9+rMpP
+N8WZ7cKbJwZpF3lf8GGaLqRmPiQg2j8qzcVynz8cIwBZP8BJVclA9GIagijY7Zcm
+z0HnTPrPoLMeyLJOTqPMfkUYA2H2eHeISkQPBeoFoiwCI5eM35UiWiLyiv9Kojn4
+BHx6MLrfKBjV13WtcRMgYm5VftsWOZ92lmHmbpj9mGgtd84JWtWxs33oG4mNRSAe
+ujf9AE5VAgMBAAECggEANwiZ/bdh2t2G0Ef9zoCCif+Z4OzAmCuAePK+gpG/TB41
+Q9eQMlkpjH5gtRKUKHWvVMNOAAhvK2FzhmoMH8rmDMkCUZAnCV2TwjxkACr1X3xT
+Y/s/cr8d7xPLL0ynXrjB0QNS3DT5Lr111/0ue3acAiN1Y2tnWc6YGFj1FsdTUg+O
+zRysrpNUp3LAK+MXIhAXMCGKOOLxpjeyrcnUokH0I8e06of1AfAHX8jTn65MG5Ex
+n9wBYPl+u2J3SjILHoqBKjcSoNILUfBN9mQGeXhoqCzwcnygDtOxIu9xgu2nCcJr
+C1R/WXoQ8Jr6wa1n0aEVXDJeOEK9kKXLTt2/I4HB2QKBgQDemLy+o2/tbFwlU2Xy
+8/tZa30kfLCAZ+kq+lE3Kkfqt3pPYzH+lfO7u/UWtavKRQRdsKsNKbpe/EdGq7c4
+YN3L1KG5JiIo3TxilUPilYacGHklfMMbEK7cs8Jebsl6rL7BgnKuqlXGY0HEEx8L
+XqIKN1RdzL04WLOiA8qDGwYp7wKBgQDNu3DECCTkTa+mZdNDRntoffkgyd0AnwPA
+PEf43BHORpKcfGwFIrf8QWRXcLdh72Yrc9o3D53GCq+NSYGPL2OiY+/3HoAy1mH1
+EBgS08qfkZBKr6+VGjWuVAlD2m2jW+AhGXMS+Lu4yzK3V+0EzlAu4WZVBUngg1//
+6ZtyvXLf+wKBgQCozmO0nvUutFJc7BYQXP5sHZvVo8mmVyb4NMSKdUH8ug/DTJKJ
+YuZnpG6FPlh9GEHrWyMc5Fw11FOpQGe+FZeeEC5k3ophOwWkLVZB6useTWDyEN9V
+Ex3IuXnZa2LX6VDwJyEZXIuX24XwUB/m22k/Hh6Y079bj8kKQJ2/NytBeQKBgQCZ
+RGMmJ8sUKqwJEyLoo8GcfvzyaHC03cI1nLMhuxGo0vq2ihsPWGYpD65pVhfIZkl/
+ZbfT/VZVC/DtGS3kNjHL8Rf8ykRHm18u6uaEYDQ73H3apjfwpK4JSaH9YuT7Jp87
+CXKpV5TCft8xp9d0FR+3TUSnYmE/WaBTTv335RuHsQKBgCFLyxzs0hM/MhCLHJ6b
+AqyNPz36Xcwsgit1Svhwm1IC6FqkSJl3cRKhp1AP5w6ktUfUGNpF/TYI3x2jCg/m
+c0nwmqi/3Cha64XKJcI4iT2+lyuE8jXovMdNiJEEKCDalpyYJbhzRaLsoSFSbiD1
+mFDl8/aNVaQKDDboSuj9AkKs
+-----END PRIVATE KEY-----
diff --git a/extras/gomemif/BUILD.bazel b/extras/hs-test/resources/envoy/envoy.log
index e69de29bb2d..e69de29bb2d 100644
--- a/extras/gomemif/BUILD.bazel
+++ b/extras/hs-test/resources/envoy/envoy.log
diff --git a/extras/hs-test/resources/envoy/proxy.yaml b/extras/hs-test/resources/envoy/proxy.yaml
new file mode 100644
index 00000000000..77da80d934d
--- /dev/null
+++ b/extras/hs-test/resources/envoy/proxy.yaml
@@ -0,0 +1,53 @@
+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
+ filter_chains:
+ - filters:
+ - name: envoy.filters.network.http_connection_manager
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ stat_prefix: ingress_http
+ route_config:
+ name: local_route
+ virtual_hosts:
+ - name: service
+ domains: ["*"]
+ routes:
+ - match:
+ prefix: "/"
+ route:
+ cluster: proxy_service
+ http_filters:
+ - name: envoy.filters.http.router
+ clusters:
+ - name: proxy_service
+ connect_timeout: 0.25s
+ type: LOGICAL_DNS
+ dns_lookup_family: V4_ONLY
+ lb_policy: ROUND_ROBIN
+ load_assignment:
+ cluster_name: proxy_service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ # following address will be generated by Addresser during test run
+ address: {{.Server}}
+ port_value: 666
+bootstrap_extensions:
+ - name: envoy.extensions.vcl.vcl_socket_interface
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.vcl.v3alpha.VclSocketInterface
+default_socket_interface: "envoy.extensions.vcl.vcl_socket_interface"
diff --git a/extras/hs-test/resources/envoy/vcl.conf b/extras/hs-test/resources/envoy/vcl.conf
new file mode 100644
index 00000000000..164435a6ae5
--- /dev/null
+++ b/extras/hs-test/resources/envoy/vcl.conf
@@ -0,0 +1,7 @@
+vcl {
+ rx-fifo-size 400000
+ tx-fifo-size 400000
+ app-scope-global
+ use-mq-eventfd
+ app-socket-api /tmp/vpp-envoy/var/run/app_ns_sockets/default
+}
diff --git a/extras/hs-test/resources/nginx/html/index.html b/extras/hs-test/resources/nginx/html/index.html
new file mode 100644
index 00000000000..6b7c97d7542
--- /dev/null
+++ b/extras/hs-test/resources/nginx/html/index.html
@@ -0,0 +1,6 @@
+<http>
+ <title>nginx docker with quic</title>
+<body>
+ <p>Greetings!</p>
+</body>
+</http>
diff --git a/extras/hs-test/resources/nginx/nginx.conf b/extras/hs-test/resources/nginx/nginx.conf
new file mode 100644
index 00000000000..99073aab1ab
--- /dev/null
+++ b/extras/hs-test/resources/nginx/nginx.conf
@@ -0,0 +1,30 @@
+master_process on;
+worker_rlimit_nofile 10240;
+worker_processes 2;
+daemon off;
+
+events {
+ use epoll;
+ worker_connections 10240;
+ accept_mutex off;
+ multi_accept off;
+}
+
+http {
+ keepalive_timeout 300s;
+ keepalive_requests 1000000;
+ sendfile on;
+ server {
+ listen 80;
+ root /usr/share/nginx;
+ index index.html index.htm;
+ location /return_ok
+ {
+ return 200 '';
+ }
+ location /64B.json
+ {
+ return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+ }
+ }
+}
diff --git a/extras/hs-test/resources/nginx/nginx_http3.conf b/extras/hs-test/resources/nginx/nginx_http3.conf
new file mode 100644
index 00000000000..2a01f714111
--- /dev/null
+++ b/extras/hs-test/resources/nginx/nginx_http3.conf
@@ -0,0 +1,26 @@
+master_process on;
+worker_processes 2;
+daemon off;
+
+events {
+ use epoll;
+ accept_mutex off;
+ multi_accept off;
+}
+
+http {
+ quic_gso on;
+ quic_retry on;
+
+ access_log logs/access.log;
+ keepalive_timeout 300s;
+ sendfile on;
+ server {
+ listen 0.0.0.0:8443 quic;
+ #listen 0.0.0.0:8443 ssl;
+ root /usr/share/nginx;
+ ssl_certificate /etc/nginx/ssl/localhost.crt;
+ ssl_certificate_key /etc/nginx/ssl/localhost.key;
+ index index.html index.htm;
+ }
+}
diff --git a/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf b/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf
new file mode 100644
index 00000000000..56debf5c290
--- /dev/null
+++ b/extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf
@@ -0,0 +1,84 @@
+master_process on;
+worker_processes 4;
+worker_rlimit_nofile 102400;
+daemon off;
+
+error_log /tmp/nginx/error.log;
+
+events {
+ use epoll;
+ worker_connections 102400;
+ accept_mutex off;
+}
+
+http {
+ include mime.types;
+ default_type application/octet-stream;
+
+ access_log off;
+
+ keepalive_timeout 300;
+ keepalive_requests 1000000;
+
+ proxy_connect_timeout 300;
+ large_client_header_buffers 4 512k;
+ client_max_body_size 3000m;
+ client_header_buffer_size 2048m;
+ client_body_buffer_size 1024m;
+ proxy_buffers 16 10240k;
+ proxy_buffer_size 10240k;
+
+ gzip on;
+
+ upstream bk {
+ server {{.Server}}:8091;
+ keepalive 30000;
+ }
+ upstream bk1 {
+ server {{.Server}}:8092;
+ keepalive 30000;
+ }
+ upstream bk2 {
+ server {{.Server}}:8093;
+ keepalive 30000;
+ }
+
+ server {
+ listen 80;
+ server_name {{.Proxy}};
+
+ server_tokens off;
+
+ proxy_redirect off;
+
+ location / {
+ root html;
+ index index.html index.htm;
+ proxy_pass http://bk;
+ proxy_set_header Connection "";
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header Host $host:$server_port;
+ chunked_transfer_encoding on;
+ proxy_http_version 1.1;
+ mirror /mimic1;
+ mirror /mimic2;
+ mirror_request_body on;
+ }
+ location /mimic1 {
+ proxy_pass http://bk1$request_uri;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header Connection "";
+ chunked_transfer_encoding on;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host:$server_port;
+ }
+ location /mimic2 {
+ proxy_pass http://bk2$request_uri;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header Host $host:$server_port;
+ proxy_set_header Connection "";
+ proxy_http_version 1.1;
+ chunked_transfer_encoding on;
+ }
+ }
+}
diff --git a/extras/hs-test/resources/nginx/nginx_server_mirroring.conf b/extras/hs-test/resources/nginx/nginx_server_mirroring.conf
new file mode 100644
index 00000000000..4056801ea13
--- /dev/null
+++ b/extras/hs-test/resources/nginx/nginx_server_mirroring.conf
@@ -0,0 +1,32 @@
+master_process on;
+worker_rlimit_nofile 10240;
+worker_processes 2;
+daemon off;
+
+events {
+ use epoll;
+ worker_connections 10240;
+ accept_mutex off;
+ multi_accept off;
+}
+
+http {
+ keepalive_timeout 300s;
+ keepalive_requests 1000000;
+ sendfile on;
+ server {
+ listen 8091;
+ listen 8092;
+ listen 8093;
+ root /usr/share/nginx;
+ index index.html index.htm;
+ location /return_ok
+ {
+ return 200 '';
+ }
+ location /64B.json
+ {
+ return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+ }
+ }
+}
diff --git a/extras/hs-test/resources/nginx/vcl.conf b/extras/hs-test/resources/nginx/vcl.conf
new file mode 100644
index 00000000000..cfcd5d2e959
--- /dev/null
+++ b/extras/hs-test/resources/nginx/vcl.conf
@@ -0,0 +1,11 @@
+vcl {
+ heapsize 64M
+ segment-size 4000000000
+ add-segment-size 4000000000
+ rx-fifo-size 4000000
+ tx-fifo-size 4000000
+ event-queue-size 100000
+
+ use-mq-eventfd
+ app-socket-api /tmp/nginx/var/run/app_ns_sockets/default
+}
diff --git a/extras/hs-test/script/build_boringssl.sh b/extras/hs-test/script/build_boringssl.sh
new file mode 100755
index 00000000000..441878a77ca
--- /dev/null
+++ b/extras/hs-test/script/build_boringssl.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cd boringssl
+cmake -GNinja -B build
+ninja -C build
diff --git a/extras/hs-test/script/build_curl.sh b/extras/hs-test/script/build_curl.sh
new file mode 100755
index 00000000000..e4258946f9e
--- /dev/null
+++ b/extras/hs-test/script/build_curl.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+wget https://github.com/stunnel/static-curl/releases/download/8.5.0/curl-static-amd64-8.5.0.tar.xz
+tar -xvf ./curl-static-amd64-8.5.0.tar.xz
+cp curl /usr/bin/curl \ No newline at end of file
diff --git a/extras/hs-test/script/build_hst.sh b/extras/hs-test/script/build_hst.sh
new file mode 100755
index 00000000000..cc2d00b6cbd
--- /dev/null
+++ b/extras/hs-test/script/build_hst.sh
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+
+if [ $(lsb_release -is) != Ubuntu ]; then
+ echo "Host stack test framework is supported only on Ubuntu"
+ exit 1
+fi
+
+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
+ 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=../..
+
+if [ "$1" == "debug" ]; then
+ VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp_debug-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
+echo "Ubuntu version is set to ${UBUNTU_VERSION}"
+
+export HST_LDPRELOAD=${VPP_BUILD_ROOT}/lib/x86_64-linux-gnu/libvcl_ldpreload.so
+echo "HST_LDPRELOAD is set to ${HST_LDPRELOAD}"
+
+export PATH=${VPP_BUILD_ROOT}/bin:$PATH
+
+bin=vpp-data/bin
+lib=vpp-data/lib
+
+mkdir -p ${bin} ${lib} || true
+rm -rf vpp-data/bin/* || true
+rm -rf vpp-data/lib/* || true
+
+cp ${VPP_BUILD_ROOT}/bin/* ${bin}
+res+=$?
+cp -r ${VPP_BUILD_ROOT}/lib/x86_64-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."
+ exit 1
+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 .
+}
+
+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
+if [ "$HST_EXTENDED_TESTS" = true ] ; then
+ docker_build hs-test/nginx-http3 nginx-http3
+ docker_build hs-test/curl curl
+fi
+
+# cleanup detached images
+images=$(docker images --filter "dangling=true" -q --no-trunc)
+if [ "$images" != "" ]; then
+ docker rmi $images
+fi
diff --git a/extras/hs-test/script/build_nginx.sh b/extras/hs-test/script/build_nginx.sh
new file mode 100755
index 00000000000..69d366aab0e
--- /dev/null
+++ b/extras/hs-test/script/build_nginx.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+cd nginx
+./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
new file mode 100644
index 00000000000..1f0205c1efb
--- /dev/null
+++ b/extras/hs-test/script/compress.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+# if failed-summary.log is not empty, exit status = 1
+if [ -s "${HS_ROOT}/summary/failed-summary.log" ]
+then
+ if [ -n "${WORKSPACE}" ]
+ then
+ echo -n "Copying docker logs..."
+ dirs=$(jq -r '.[0] | .SpecReports[] | select(.State == "failed") | .LeafNodeText' ${HS_ROOT}/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."
+
+ 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."
+
+ else
+ echo "Not compressing files in temporary directories from test runs."
+ fi
+ exit 1
+fi
diff --git a/extras/hs-test/script/nginx_ldp.sh b/extras/hs-test/script/nginx_ldp.sh
new file mode 100755
index 00000000000..4a22e14aaf7
--- /dev/null
+++ b/extras/hs-test/script/nginx_ldp.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+LD_PRELOAD=$LDP $@ 2>&1 > /proc/1/fd/1
diff --git a/extras/hs-test/suite_nginx_test.go b/extras/hs-test/suite_nginx_test.go
new file mode 100644
index 00000000000..c559496e71b
--- /dev/null
+++ b/extras/hs-test/suite_nginx_test.go
@@ -0,0 +1,134 @@
+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
new file mode 100644
index 00000000000..625dca9f3cf
--- /dev/null
+++ b/extras/hs-test/suite_no_topo_test.go
@@ -0,0 +1,110 @@
+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
new file mode 100644
index 00000000000..85b90911c2f
--- /dev/null
+++ b/extras/hs-test/suite_ns_test.go
@@ -0,0 +1,119 @@
+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
new file mode 100644
index 00000000000..ebf0f9b3cbc
--- /dev/null
+++ b/extras/hs-test/suite_tap_test.go
@@ -0,0 +1,84 @@
+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
new file mode 100644
index 00000000000..d47bf8c52a9
--- /dev/null
+++ b/extras/hs-test/suite_veth_test.go
@@ -0,0 +1,144 @@
+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
new file mode 100644
index 00000000000..398e2b39edb
--- /dev/null
+++ b/extras/hs-test/test
@@ -0,0 +1,94 @@
+#!/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
+
+jq -r '.[0] | .SpecReports[] | select((.State == "failed") or (.State == "timedout") or (.State == "panicked")) | select(.Failure != null) | "TestName: \(.LeafNodeText)\nSuite:\n\(.Failure.Location.FileName)\nMessage:\n\(.Failure.Message)\n Full Stack Trace:\n\(.Failure.Location.FullStackTrace)\n"' summary/report.json > summary/failed-summary.log \
+ && echo "Summary generated -> summary/failed-summary.log" \ No newline at end of file
diff --git a/extras/hs-test/tools/http_server/http_server.go b/extras/hs-test/tools/http_server/http_server.go
new file mode 100644
index 00000000000..d2ab3851643
--- /dev/null
+++ b/extras/hs-test/tools/http_server/http_server.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+)
+
+func main() {
+ if len(os.Args) < 3 {
+ fmt.Println("arg expected")
+ os.Exit(1)
+ }
+
+ http.HandleFunc("/httpTestFile", func(w http.ResponseWriter, r *http.Request) {
+ file, _ := os.Open("httpTestFile" + os.Args[2])
+ defer file.Close()
+ io.Copy(w, file)
+ })
+ err := http.ListenAndServe(os.Args[1], nil)
+ if err != nil {
+ fmt.Printf("%v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/extras/hs-test/topo-containers/2peerVeth.yaml b/extras/hs-test/topo-containers/2peerVeth.yaml
new file mode 100644
index 00000000000..e1591fb9019
--- /dev/null
+++ b/extras/hs-test/topo-containers/2peerVeth.yaml
@@ -0,0 +1,25 @@
+---
+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
+ - name: "server-app"
+ volumes:
+ - <<: *server-vol
+ - name: "client-app"
+ volumes:
+ - <<: *client-vol
+
diff --git a/extras/hs-test/topo-containers/nginxProxyAndServer.yaml b/extras/hs-test/topo-containers/nginxProxyAndServer.yaml
new file mode 100644
index 00000000000..cc6b780bafc
--- /dev/null
+++ b/extras/hs-test/topo-containers/nginxProxyAndServer.yaml
@@ -0,0 +1,20 @@
+---
+volumes:
+ - volume: &shared-vol-proxy
+ host-dir: "$HST_VOLUME_DIR/shared-vol-proxy"
+
+containers:
+ - name: "vpp-proxy"
+ volumes:
+ - <<: *shared-vol-proxy
+ container-dir: "/tmp/vpp"
+ is-default-work-dir: true
+ - name: "nginx-proxy"
+ volumes:
+ - <<: *shared-vol-proxy
+ 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"
diff --git a/extras/hs-test/topo-containers/ns.yaml b/extras/hs-test/topo-containers/ns.yaml
new file mode 100644
index 00000000000..2298ad232c2
--- /dev/null
+++ b/extras/hs-test/topo-containers/ns.yaml
@@ -0,0 +1,27 @@
+---
+volumes:
+ - volume: &shared-vol
+ host-dir: "$HST_VOLUME_DIR/shared-vol"
+
+# $HST_DIR will be replaced during runtime by path to hs-test directory
+containers:
+ - name: "vpp"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/vpp"
+ is-default-work-dir: true
+ - name: "envoy"
+ 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.21-latest"
+ extra-args: "--concurrency 2 -c /etc/envoy/envoy.yaml"
+ is-optional: true
diff --git a/extras/hs-test/topo-containers/single.yaml b/extras/hs-test/topo-containers/single.yaml
new file mode 100644
index 00000000000..b6970c517bd
--- /dev/null
+++ b/extras/hs-test/topo-containers/single.yaml
@@ -0,0 +1,47 @@
+---
+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: "nginx"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/nginx"
+ is-default-work-dir: true
+ image: "hs-test/nginx-ldp"
+ is-optional: true
+
+ - name: "nginx-http3"
+ volumes:
+ - <<: *shared-vol
+ container-dir: "/tmp/nginx"
+ is-default-work-dir: true
+ - host-dir: $HST_DIR/resources/cert
+ container-dir: "/etc/nginx/ssl"
+ image: "hs-test/nginx-http3"
+ is-optional: true
+
+ - name: "ab"
+ image: "jordi/ab"
+ is-optional: true
+ run-detached: false
+
+ - name: "wrk"
+ image: "skandyla/wrk"
+ is-optional: true
+ run-detached: false
+
+ - name: "curl"
+ vars:
+ - name: LD_LIBRARY_PATH
+ value: "/usr/local/lib"
+ image: "hs-test/curl"
+ is-optional: true
+ run-detached: false \ No newline at end of file
diff --git a/extras/hs-test/topo-network/2peerVeth.yaml b/extras/hs-test/topo-network/2peerVeth.yaml
new file mode 100644
index 00000000000..f991d8b3701
--- /dev/null
+++ b/extras/hs-test/topo-network/2peerVeth.yaml
@@ -0,0 +1,25 @@
+---
+devices:
+ - name: "hsns"
+ type: "netns"
+
+ - name: "srv"
+ type: "veth"
+ preset-hw-address: "00:00:5e:00:53:01"
+ peer:
+ name: "srv_veth"
+ netns: "hsns"
+
+ - name: "cln"
+ type: "veth"
+ peer:
+ name: "cln_veth"
+ netns: "hsns"
+
+ - name: "br"
+ type: "bridge"
+ netns: "hsns"
+ interfaces:
+ - srv_veth
+ - cln_veth
+
diff --git a/extras/hs-test/topo-network/2taps.yaml b/extras/hs-test/topo-network/2taps.yaml
new file mode 100644
index 00000000000..f5dd8e2adda
--- /dev/null
+++ b/extras/hs-test/topo-network/2taps.yaml
@@ -0,0 +1,18 @@
+---
+devices:
+ - name: "hstcln"
+ type: "tap"
+ ip4:
+ network: 1
+ peer:
+ name: ""
+ ip4:
+ network: 1
+ - name: "hstsrv"
+ type: "tap"
+ ip4:
+ network: 2
+ peer:
+ name: ""
+ ip4:
+ network: 2
diff --git a/extras/hs-test/topo-network/ns.yaml b/extras/hs-test/topo-network/ns.yaml
new file mode 100644
index 00000000000..018c329f77e
--- /dev/null
+++ b/extras/hs-test/topo-network/ns.yaml
@@ -0,0 +1,23 @@
+---
+devices:
+ - name: "cln"
+ type: "netns"
+
+ - name: "srv"
+ type: "netns"
+
+ - name: "hclnvpp"
+ type: "veth"
+ peer:
+ name: "cln"
+ netns: "cln"
+ ip4:
+ network: 1
+
+ - name: "hsrvvpp"
+ type: "veth"
+ peer:
+ name: "srv"
+ netns: "srv"
+ ip4:
+ network: 2
diff --git a/extras/hs-test/topo-network/tap.yaml b/extras/hs-test/topo-network/tap.yaml
new file mode 100644
index 00000000000..acf14958ba3
--- /dev/null
+++ b/extras/hs-test/topo-network/tap.yaml
@@ -0,0 +1,10 @@
+---
+devices:
+ - name: "htaphost"
+ type: "tap"
+ ip4:
+ network: 1
+ peer:
+ name: ""
+ ip4:
+ network: 1
diff --git a/extras/hs-test/topo.go b/extras/hs-test/topo.go
new file mode 100644
index 00000000000..6cb294511b3
--- /dev/null
+++ b/extras/hs-test/topo.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+ "fmt"
+)
+
+type NetDevConfig map[string]interface{}
+type ContainerConfig map[string]interface{}
+type VolumeConfig map[string]interface{}
+
+type YamlTopology struct {
+ Devices []NetDevConfig `yaml:"devices"`
+ Containers []ContainerConfig `yaml:"containers"`
+ Volumes []VolumeConfig `yaml:"volumes"`
+}
+
+func addAddress(device, address, ns string) error {
+ c := []string{"ip", "addr", "add", address, "dev", device}
+ cmd := appendNetns(c, ns)
+ err := cmd.Run()
+ if err != nil {
+ return fmt.Errorf("failed to set ip address for %s: %v", device, err)
+ }
+ return nil
+}
diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go
new file mode 100644
index 00000000000..304dd4c241b
--- /dev/null
+++ b/extras/hs-test/utils.go
@@ -0,0 +1,80 @@
+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/vars b/extras/hs-test/vars
new file mode 100644
index 00000000000..d1ca078fe21
--- /dev/null
+++ b/extras/hs-test/vars
@@ -0,0 +1,7 @@
+export VPP_WS=../../
+
+export HST_LDPRELOAD=${VPP_WS}/build-root/build-vpp-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so
+export PATH=${VPP_WS}/build-root/build-vpp-native/vpp/bin:$PATH
+
+export UBUNTU_VERSION=$(lsb_release -rs)
+export HST_EXTENDED_TESTS=false
diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go
new file mode 100644
index 00000000000..fdcd60ad503
--- /dev/null
+++ b/extras/hs-test/vcl_test.go
@@ -0,0 +1,156 @@
+package main
+
+import (
+ "fmt"
+ "time"
+)
+
+func init() {
+ registerVethTests(XEchoVclClientUdpTest, XEchoVclClientTcpTest, XEchoVclServerUdpTest,
+ XEchoVclServerTcpTest, VclEchoTcpTest, VclEchoUdpTest, VclRetryAttachTest)
+}
+
+func getVclConfig(c *Container, ns_id_optional ...string) string {
+ var s Stanza
+ ns_id := "default"
+ 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")
+ 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))
+ }
+ return s.close().toString()
+}
+
+func XEchoVclClientUdpTest(s *VethsSuite) {
+ s.testXEchoVclClient("udp")
+}
+
+func XEchoVclClientTcpTest(s *VethsSuite) {
+ s.testXEchoVclClient("tcp")
+}
+
+func (s *VethsSuite) testXEchoVclClient(proto string) {
+ port := "12345"
+ 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)
+
+ 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")
+}
+
+func XEchoVclServerUdpTest(s *VethsSuite) {
+ s.testXEchoVclServer("udp")
+}
+
+func XEchoVclServerTcpTest(s *VethsSuite) {
+ s.testXEchoVclServer("tcp")
+}
+
+func (s *VethsSuite) testXEchoVclServer(proto string) {
+ port := "12345"
+ srvVppCont := s.getContainerByName("server-vpp")
+ srvAppCont := s.getContainerByName("server-app")
+
+ 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)
+
+ 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")
+}
+
+func (s *VethsSuite) testVclEcho(proto string) {
+ port := "12345"
+ 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)
+
+ serverVeth := s.getInterfaceByName(serverInterfaceName)
+ serverVethAddress := serverVeth.ip4AddressString()
+
+ 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)
+}
+
+func VclEchoTcpTest(s *VethsSuite) {
+ s.testVclEcho("tcp")
+}
+
+func VclEchoUdpTest(s *VethsSuite) {
+ s.testVclEcho("udp")
+}
+
+func VclRetryAttachTest(s *VethsSuite) {
+ s.testRetryAttach("tcp")
+}
+
+func (s *VethsSuite) testRetryAttach(proto string) {
+ srvVppContainer := s.getTransientContainerByName("server-vpp")
+
+ echoSrvContainer := s.getContainerByName("server-app")
+
+ echoSrvContainer.createFile("/vcl.conf", getVclConfig(echoSrvContainer))
+
+ 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.")
+
+ serverVeth := s.getInterfaceByName(serverInterfaceName)
+ serverVethAddress := serverVeth.ip4AddressString()
+
+ 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.")
+
+ // Stop server-vpp-instance, start it again and then run vcl-test-client once more
+ srvVppContainer.vppInstance.disconnect()
+ stopVppCommand := "/bin/bash -c 'ps -C vpp_main -o pid= | xargs kill -9'"
+ srvVppContainer.exec(stopVppCommand)
+
+ s.setupServerVpp()
+
+ 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.")
+}
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
new file mode 100644
index 00000000000..11f68a61eac
--- /dev/null
+++ b/extras/hs-test/vppinstance.go
@@ -0,0 +1,471 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "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
+ 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
+ apiStream api.Stream
+ 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 {
+ 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))
+ }
+
+ ch, err := conn.NewStream(
+ context.Background(),
+ core.WithRequestSize(50),
+ core.WithReplySize(50),
+ core.WithReplyTimeout(time.Second*10))
+ 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
+ }
+
+ // 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
+ }
+
+ 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.apiStream.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/lcov/README.md b/extras/lcov/README.md
deleted file mode 100644
index ae57fe53477..00000000000
--- a/extras/lcov/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Code coverage analysis with lcov {#lcov_code_coverage}
-
-## Prerequisites
-
-The Linux gcov and lcov tools are fussy about gcc / g++ compiler
-versions. As of this writing, Ubuntu 18.04 gcov / lcov work with
-these toolchain versions:
-
- $ gcc --version
- gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
- $ g++ --version
- g++ (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0
-
-Refer to
-https://askubuntu.com/questions/26498/how-to-choose-the-default-gcc-and-g-version for information on how to install multiple gcc / g++ versions, and
-switch between them.
-
-You'll need to install the following additional packages:
-
- $ sudo apt-get install gcovr ggcov lcov
-
-## Compile an instrumented vpp image
-
-Two ways:
-
- $ cd <workspace-root>
- $ make test-gcov
- $ ## interrupt compilation after building the image
-
-or
- $ cd <workspace-root>/build-root
- $ make PLATFORM=vpp TAG=vpp_gcov vpp-install
-
-## Initialize the lcov database
-
- $ cd <workspace-root>
- $ ./extras/lcov/lcov_prep
- $ make test-gcov or make TEST=my_test test-gcov
- $ # repeat or vary as desired to increase reported coverage
- $ # Generate the report:
- $ ./extras/lcov/lcov_post
-
-You can run vpp manually, do anything you like. Results are cumulative
-until you re-run the "prep" script.
-
-## Look at the results
-
-Point a browser at file:///<workspace-root>/build-root/html/index.html
diff --git a/extras/lcov/lcov_post b/extras/lcov/lcov_post
deleted file mode 100755
index 1aaf9865d24..00000000000
--- a/extras/lcov/lcov_post
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-set -e
-
-cd build-root
-rm -rf html
-mkdir html
-lcov --no-checksum --directory . --capture --output-file out.info
-lcov --remove out.info \
- "/usr/include/*" "*/build-root/*" "/opt/*" "/usr/lib/*" \
- -o filtered.info
-genhtml filtered.info -o html
diff --git a/extras/lcov/lcov_prep b/extras/lcov/lcov_prep
deleted file mode 100755
index 1212d0470a2..00000000000
--- a/extras/lcov/lcov_prep
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-set -e
-
-cd build-root
-rm -rf html
-lcov --capture --initial --directory . --output-file lcov.out
diff --git a/extras/libmemif/CMakeLists.txt b/extras/libmemif/CMakeLists.txt
index 7f4915b5d70..1526abdce3a 100644
--- a/extras/libmemif/CMakeLists.txt
+++ b/extras/libmemif/CMakeLists.txt
@@ -18,6 +18,35 @@ set(CMAKE_C_STANDARD 11)
include(CheckCCompilerFlag)
include(CheckFunctionExists)
+find_package(Git REQUIRED)
+
+include(ExternalProject)
+set(UNITY unity_project)
+
+ExternalProject_Add(
+ unity_project
+ GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git
+ GIT_TAG cf949f45ca6d172a177b00da21310607b97bc7a7
+ PREFIX ${PROJECT_BINARY_DIR}/external/${UNITY}
+ INSTALL_COMMAND cmake --install . --prefix ${PROJECT_BINARY_DIR}
+
+)
+set_source_files_properties(
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/src/unity.c
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/extras/fixture/src/unity_fixture.c
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/extras/memory/src/unity_memory.c
+ PROPERTIES GENERATED TRUE)
+add_library(unity STATIC
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/src/unity.c
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/extras/fixture/src/unity_fixture.c
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/extras/memory/src/unity_memory.c
+)
+target_include_directories(unity PUBLIC
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/src/
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/extras/fixture/src/
+ ${PROJECT_BINARY_DIR}/external/${UNITY}/src/${UNITY}/extras/memory/src/
+)
+add_dependencies(unity unity_project)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
@@ -32,6 +61,17 @@ set(CMAKE_INSTALL_MESSAGE NEVER)
find_package(Threads REQUIRED)
include_directories(${CMAKE_THREADS_INCLUDE_DIRS})
+if(DEFINED LIBMEMIF_CACHELINE_SIZE)
+ # Cache line size assigned via cmake args
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
+ set(LIBMEMIF_CACHELINE_SIZE 128)
+else()
+ set(LIBMEMIF_CACHELINE_SIZE 64)
+endif()
+
+message(STATUS "System Architecture: ${CMAKE_SYSTEM_PROCESSOR}")
+message(STATUS "Libmemif Cacheline Size: ${LIBMEMIF_CACHELINE_SIZE}")
+
check_function_exists(memfd_create HAVE_MEMFD_CREATE)
if(${HAVE_MEMFD_CREATE})
add_definitions(-DHAVE_MEMFD_CREATE)
@@ -39,18 +79,12 @@ endif()
include_directories(src)
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
-find_package(Check 0.10.0)
-if (CHECK_FOUND)
- include_directories(${CHECK_INCLUDE_DIR})
- add_definitions(-DMEMIF_UNIT_TEST)
- add_subdirectory(test)
- enable_testing()
-endif ()
-
add_subdirectory(src)
add_subdirectory(examples)
+enable_testing()
+include(CTest)
+add_subdirectory(test)
##############################################################################
# Packaging
##############################################################################
diff --git a/extras/libmemif/dockerfile b/extras/libmemif/dockerfile
index 81012b0d5fd..c80ca5bdad6 100644
--- a/extras/libmemif/dockerfile
+++ b/extras/libmemif/dockerfile
@@ -1,20 +1,20 @@
FROM ubuntu:xenial
RUN apt-get update && \
- apt-get install -y git build-essential autoconf pkg-config libtool sudo check
+ apt-get install -y git build-essential autoconf pkg-config libtool sudo
RUN rm -rf /var/lib/apt/lists/*
RUN mkdir /libmemif
ADD . /libmemif
WORKDIR /libmemif
-RUN ./bootstrap
-RUN ./configure
+RUN mkdir build
+RUN cmake ..
+WORKDIR /libmemif/build
RUN make
-RUN make install
RUN mkdir /run/vpp
RUN ulimit -c unlimited
-CMD ./.libs/icmpr-epoll
+CMD ./examples/icmp_responder
diff --git a/extras/libmemif/docs/buildinstructions_doc.md b/extras/libmemif/docs/buildinstructions_doc.md
deleted file mode 100644
index c2eecba4d96..00000000000
--- a/extras/libmemif/docs/buildinstructions_doc.md
+++ /dev/null
@@ -1,49 +0,0 @@
-## Build Instructions {#libmemif_build_doc}
-
-#### Install dependencies
-```
-# sudo apt-get install -y git cmake autoconf pkg_config libtool check
-```
-
-Libmemif is now part of VPP repository. Follow fd.io wiki to pull source code from VPP repository.
-[https://wiki.fd.io/view/VPP/Pulling,_Building,_Running,_Hacking_and_Pushing_VPP_Code#Pushing_Patches](https://wiki.fd.io/view/VPP/Pulling,_Building,_Running,_Hacking_and_Pushing_VPP_Code#Pushing_Patches)
-
-Libmemif is located under extras/libmemif. From extras/libmemif:
-```
-# mkdir build
-# cd build
-# cmake ..
-# make install
-```
-
-#### Verify installation:
-```
-build# ./examples/icmpr-epoll
-```
-Use _help_ command to display build information and commands:
-```
-LIBMEMIF EXAMPLE APP: ICMP_Responder
-==============================
-libmemif version: 3.0
-memif version: 512
- use CTRL+C to exit
-MEMIF DETAILS
-==============================
- interface name: memif_connection
- app name: ICMP_Responder
- remote interface name:
- remote app name:
- id: 0
- secret: (null)
- role: slave
- mode: ethernet
- socket filename: /run/vpp/memif.sock
- socket filename: /run/vpp/memif.sock
- rx queues:
- tx queues:
- link: down
-```
-
-#### Examples
-
-Once the library is built/installed, refer to @ref libmemif_examples_doc and @ref libmemif_gettingstarted_doc for additional information on basic use cases and API usage.
diff --git a/extras/libmemif/docs/buildinstructions_doc.rst b/extras/libmemif/docs/buildinstructions_doc.rst
new file mode 100644
index 00000000000..6609f7a1926
--- /dev/null
+++ b/extras/libmemif/docs/buildinstructions_doc.rst
@@ -0,0 +1,77 @@
+.. _libmemif_build_doc:
+
+Build Instructions
+==================
+
+Install dependencies
+--------------------
+
+::
+
+ sudo apt-get install -y git cmake autoconf pkg_config libtool
+
+Libmemif is now part of VPP repository. Follow fd.io wiki to pull source
+code from VPP repository.
+https://wiki.fd.io/view/VPP/Pulling,_Building,_Running,_Hacking_and_Pushing_VPP_Code#Pushing_Patches
+
+Libmemif is located under extras/libmemif. From the vpp workspace root directory::
+
+ mkdir -p extras/libmemif/build
+ cd extras/libmemif/build
+ cmake ..
+ make install
+
+Verify installation:
+--------------------
+
+::
+
+ ./examples/icmp_responder -?
+
+Use ``-?`` flag to display help::
+
+ LIBMEMIF EXAMPLE APP: icmp_responder_example
+ ==============================
+ libmemif version: 4.0, memif version: 2.0
+ ==============================
+ In this example, memif endpoint connects to an external application.
+ The example application can resolve ARP and reply to ICMPv4 packets.
+ The program will exit once the interface is disconnected.
+ ==============================
+ Usage: icmp_responder [OPTIONS]
+
+ Options:
+ -r Interface role <slave|master>. Default: slave
+ -s Socket path. Supports abstract socket using @ before the path. Default: /run/vpp/memif.sock
+ -i Interface id. Default: 0
+ -a IPv4 address. Default: 192.168.1.1
+ -h Mac address. Default: aa:aa:aa:aa:aa:aa
+ -? Show help and exit.
+ -v Show libmemif and memif version information and exit.
+
+Running tests:
+--------------
+
+Tests needs to their proper functioning Unity framework which is depended externally and cloned from official git repository::
+
+ mkdir -p extras/libmemif/build
+ cd extras/libmemif/build
+ cmake ..
+ make
+ ctest
+
+``ctest`` will execute the tests and print out brief information about test suites with their statuses.
+
+In case we want verbose: ::
+
+ ctest --verbose
+ ctest --extra-verbose
+
+If there are any needs to debug tests, just add to cmake command ``-DCMAKE_BUILD_TYPE=Debug`` flag.
+
+Use Cases
+---------
+
+Once the library is built/installed, refer to :ref:`libmemif_gettingstarted_doc`
+and :ref:`libmemif_examples_doc` for additional information on basic use cases
+and API usage.
diff --git a/extras/libmemif/docs/devperftest_doc.md b/extras/libmemif/docs/devperftest_doc.md
deleted file mode 100644
index 4c210f5b514..00000000000
--- a/extras/libmemif/docs/devperftest_doc.md
+++ /dev/null
@@ -1,94 +0,0 @@
-## Development performance test {#libmemif_devperftest_doc}
-
-Simple test cases using ICMP. icmpr-epoll example app generates and transmits packets over memif interface.
-
-#### TC1: LIB-VPP
-
-Start icmpr-epoll example app and VPP.
-
-VPP-side config:
-```
-DBGvpp# create interface memif id 0 master
-DBGvpp# set int state memif0/0 up
-DBGvpp# set int ip address memif0/0 192.168.1.1/24
-```
-icmpr-epoll:
-```
-conn 0 0 1
-```
-> Last argument specifies interrupt function to use. This function only responds to ARP requests. This is important because, packet generation and transmitting is handled by a separate thread. Calling memif_tx_burst from multiple threads writing on same queue could transmit uninitialized buffers.
-Once connection is established, you can send ping from VPP to icmpr-epoll app to learn its mac address.
-```
-DBGvpp# ping 192.168.1.2
-```
-> There should be no ICMP response. Only ARP response.
-Now send ICMP requests from icmpr-epoll:
-```
-send <index> <num-of-packets> <ip_daddr> <hw_daddr>
-send 0 5 192.168.1.1 02:fe:ff:ff:ff:ff
-```
-this command will create new thread which will generate icmp packets and transmit them over memif connection with specified index. Once the sequence is finished status will be printed.
-
-###### Example results (overview of test data)
-
-(This test was run with modification in VPP-memif plugin. The modification disallows memif tx node to allocate last ring buffer)
-lib-tx: 200M (if ring full don't drop packets)
-vpp-rx: 200M
-vpp-tx: 200M - 50K (if ring full drop packets)
-lib-rx: =vpp-tx
-drop: ~0.025% (full ring)
-pps: ~650K
-multiple interfaces:
-pps: divided
-drop: constant
-
-#### TC2: LIB-LIB
-
-This test case will not drop packets if memif ring is full. Instead it will loop until all required packets have been sent.
-
-Start two instances of icmpr-epoll example app.
-instance 1:
-```
-conn 0 1 0
-```
-instance 2:
-```
-conn 0 0 1
-send 0 5 192.168.1.1 aa:aa:aa:aa:aa:aa
-```
-> icmpr-epoll example app doesn't check ip or mac address so as long as the format is correct you can type anything as ip_daddr and hw_daddr arguments.
-
-###### Example results (overview of test data)
-
-lib1-tx: 200M (if ring full don't drop packets)
-lib2-rx: 200M
-lib2-tx: 200M (if ring full don't drop packets)
-lib1-rx: 200M
-drop: obsolete
-pps: 4.5M
-multiple interfaces:
-not tested (expected same as TC1)
-
-#### TC3: LIB-LIB
-
-Start two instances of icmpr-epoll example app.
-instance 1:
-```
-conn 0 1
-```
-instance 2:
-```
-conn 0 0 1
-send 0 5 192.168.1.1 aa:aa:aa:aa:aa:aa
-```
-
-###### Example results (overview of test data)
-
-lib1-tx: 200M (if ring full don't drop packets)
-lib2-rx: 200M
-lib2-tx: 169626182 (if ring full drop packets)
-lib1-rx: =lib2-tx
-drop: ~15%
-pps: ~6M
-multiple interfaces:
-not tested (expected same as TC1)
diff --git a/extras/libmemif/docs/gettingstarted_doc.md b/extras/libmemif/docs/gettingstarted_doc.md
deleted file mode 100644
index abf9a1a06fd..00000000000
--- a/extras/libmemif/docs/gettingstarted_doc.md
+++ /dev/null
@@ -1,223 +0,0 @@
-## Getting started {#libmemif_gettingstarted_doc}
-
-#### Concept (Connecting to VPP)
-
-For detailed information on api calls and structures please refer to @ref libmemif.h.
-
-1. Initialize memif
- - Declare callback function handling file descriptor event polling.
-```C
-int
-control_fd_update (int fd, uint8_t events)
-{
-...
-}
-```
- - Call memif initialization function. memif\_init
-```C
-err = memif_init (control_fd_update, APP_NAME, NULL, NULL);
-```
-
-> If event occurs on any file descriptor returned by this callback, call memif\_control\_fd\_handler function. Since version 2.0, last two optional arguments are used to specify custom memory allocation.
-```C
-memif_err = memif_control_fd_handler (evt.data.fd, events);
-```
-> If callback function parameter for memif\_init function is set to NULL, libmemif will handle file descriptor event polling.
- Api call memif\_poll\_event will call epoll\_pwait with user defined timeout to poll event on file descriptors opened by libmemif.
-```C
-/* main loop */
- while (1)
- {
- if (memif_poll_event (-1) < 0)
- {
- DBG ("poll_event error!");
- }
- }
-```
-
-> Memif initialization function will initialize internal structures and create timer file descriptor, which will be used for sending periodic connection requests. Timer is disarmed if no memif interface is created.
-
-2. Creating interface
- - Declare memif connection handle.
-```C
-memif_conn_handle_t c;
-```
-> example app uses struct that contains connection handle, rx/tx buffers and other connection specific information.
-
- - Specify connection arguments.
-```C
-memif_conn_args_t args;
-memset (&args, 0, sizeof (args));
-args.is_master = is_master;
-args.log2_ring_size = 10;
-args.buffer_size = 2048;
-args.num_s2m_rings = 2;
-args.num_m2s_rings = 2;
-strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
-args.mode = 0;
-args.interface_id = 0;
-```
- - Declare callback functions called on connected/disconnected/interrupted status changed.
-```C
-int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
-...
-}
-
-int
-on_disconnect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif connected!");
- return 0;
-}
-```
- - Call memif interface create function. memif\_create
-```C
-err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt, &ctx[index]);
-```
-> If connection is in slave mode, arms timer file descriptor.
-> If on interrupt callback is set to NULL, user will not be notified about interrupt. Use memif\_get\_queue\_efd call to get interrupt file descriptor for specific queue.
-```C
-int fd = -1;
-err = memif_get_queue_efd (c->conn, data->qid, &fd);
-```
-
-3. Connection establishment
- - User application will poll events on all file descriptors returned in memif\_control\_fd\_update\_t callback.
- - On event call memif\_control\_fd\_handler.
- - Everything else regarding connection establishment will be done internally.
- - Once connection has been established, a callback will inform the user about connection status change.
-
-4. Interrupt packet receive
- - If event is polled on interrupt file descriptor, libmemif will call memif\_interrupt\_t callback specified for every connection instance.
-```C
-int
-on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
-...
-}
-```
-
-6. Memif buffers
- - Packet data are stored in memif\_buffer\_t. Pointer _data_ points to shared memory buffer, and unsigned integer *_len* contains buffer length.
- - flags: MEMIF\_BUFFER\_FLAG\_NEXT states that the buffer is not large enough to contain whole packet, so next buffer contains the rest of the packet. (chained buffers)
-```C
-typedef struct
-{
- uint16_t desc_index;
- uint32_t len;
- uint8_t flags;
- void *data;
-} memif_buffer_t;
-```
-
-5. Packet receive
- - Api call memif\_rx\_burst will set all required fields in memif buffers provided by user application, dequeue received buffers and consume interrupt event on receive queue. The event is not consumed, if memif_rx_burst fails.
-```C
-err = memif_rx_burst (c->conn, qid, c->bufs, MAX_MEMIF_BUFS, &rx);
-```
- - User application can then process packets.
- - Api call memif\_refill\_queue will enqueue rx buffers.
-```C
-err = memif_refill_queue (c->conn, qid, rx);
-```
-
-6. Packet transmit
- - Api call memif\_buffer\_alloc will find free tx buffers and set all required fields in memif buffers provided by user application.
-```C
-err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, n, &r);
-```
- - User application can populate shared memory buffers with packets.
- - Api call memif\_tx\_burst will enqueue tx buffers
-```C
-err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r);
-```
-
-7. Helper functions
- - Memif version
-```C
-uint16_t memif_ver = memif_get_version ();
-```
- - Memif details
- - Api call memif\_get\_details will return details about connection.
-```C
-err = memif_get_details (c->conn, &md, buf, buflen);
-```
- - Memif error messages
- - Every api call returns error code (integer value) mapped to error string.
- - Call memif\_strerror will return error message assigned to specific error code.
-```C
-if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_get_details: %s", memif_strerror (err));
-```
- - Not all syscall errors are translated to memif error codes. If error code 1 (MEMIF\_ERR\_SYSCALL) is returned then libmemif needs to be compiled with -DMEMIF_DBG flag to print error message. Use _make -B_ to rebuild libmemif in debug mode.
-
-#### Example app (libmemif fd event polling):
-
-- @ref extras/libmemif/examples/icmp_responder
-
-> Optional argument: transmit queue id.
-```
-icmpr 1
-```
-> Set transmit queue id to 1. Default is 0.
-> Application will create memif interface in slave mode and try to connect to VPP. Exit using Ctrl+C. Application will handle SIGINT signal, free allocated memory and exit with EXIT_SUCCESS.
-
-#### Example app:
-
-ICMP Responder custom fd event polling.
-
-- @ref extras/libmemif/examples/icmp_responder-epoll
-
-#### Example app (multi-thread queue polling)
-
-ICMP Responder multi-thread.
-- @ref extras/libmemif/examples/icmp_responder-mt
-
-> Simple example of libmemif multi-thread usage. Connection establishment is handled by main thread. There are two rx/tx queues in this example. One in polling mode and second in interrupt mode.
-
-VPP config:
-```
-# create interface memif id 0 master
-# set int state memif0 up
-# set int ip address memif0 192.168.1.1/24
-# ping 192.168.1.2
-```
-For multiple rings (queues) support run VPP with worker threads:
-example startup.conf:
-```
-unix {
- interactive
- nodaemon
- full-coredump
-}
-
-cpu {
- workers 2
-}
-```
-VPP config:
-```
-# create interface memif id 0 master
-# set int state memif0 up
-# set int ip address memif0 192.168.1.1/24
-# ping 192.168.1.2
-```
-> Master mode queue number is limited by worker threads. Slave mode interface needs to specify number of queues.
-```
-# create memif id 0 slave rx-queues 2 tx-queues 2
-```
-> Example applications use VPP default socket file for memif: /run/vpp/memif.sock
-> For master mode, socket directory must exist prior to memif\_create call.
-
-#### Unit tests
-
-Unit tests use [Check](https://libcheck.github.io/check/index.html) framework. This framework must be installed in order to build *unit\_test* binary.
-Ubuntu/Debian:
-```
-sudo apt-get install check
-```
-[More platforms](https://libcheck.github.io/check/web/install.html)
-
diff --git a/extras/libmemif/docs/gettingstarted_doc.rst b/extras/libmemif/docs/gettingstarted_doc.rst
new file mode 100644
index 00000000000..f576fe25145
--- /dev/null
+++ b/extras/libmemif/docs/gettingstarted_doc.rst
@@ -0,0 +1,234 @@
+.. _libmemif_gettingstarted_doc:
+
+Getting started
+===============
+
+For detailed information on api calls and structures please refer to
+``libmemif.h``.
+
+Start by creating a memif socket. Memif socket represents UNIX domain
+socket and interfaces assigned to use this socket. Memif uses UNIX domain
+socket to communicate with other memif drivers.
+
+First fill out the ``memif_socket_args`` struct. The minimum required
+configuration is the UNIX socket path. > Use ``@`` or ``\0`` at the
+beginning of the path to use abstract socket.
+
+.. code:: c
+
+ memif_socket_args_t sargs;
+
+ strncpy(sargs.path, socket_path, sizeof(sargs.path));
+
+.. code:: c
+
+ memif_socket_handle_t memif_socket;
+
+ memif_create_socket(&memif_socket, &sargs, &private_data);
+
+Once you have created your socket, you can create memif interfaces on
+this socket. Fill out the ``memif_conn_args`` struct. Then call
+``memif_create()``.
+
+.. code:: c
+
+ memif_conn_args_t cargs;
+
+ /* Assign your socket handle */
+ cargs.socket = memif_socket;
+
+.. code:: c
+
+ memif_conn_handle_t conn;
+
+ /* Assign callbacks */
+ memif_create (&conn, &cargs, on_connect_cb, on_disconnect_cb, on_interrupt_cb, &private_data);
+
+Now start the polling events using libmemifs builtin polling.
+
+.. code:: c
+
+ do {
+ err = memif_poll_event(memif_socket, /* timeout -1 = blocking */ -1);
+ } while (err == MEMIF_ERR_SUCCESS);
+
+Polling can be canceled by calling ``memif_cancel_poll_event()``.
+
+.. code:: c
+
+ memif_cancel_poll_event (memif_socket);
+
+On link status change ``on_connect`` and ``on_disconnect`` callbacks are
+called respectively. Before you can start transmitting data you, first
+need to call ``memif_refill_queue()`` for each RX queue to initialize
+this queue.
+
+.. code:: c
+
+ int on_connect (memif_conn_handle_t conn, void *private_ctx)
+ {
+ my_private_data_t *data = (my_private_data_t *) private_ctx;
+
+ err = memif_refill_queue(conn, 0, -1, 0);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_refill_queue: %s", memif_strerror(err));
+ return err;
+ }
+
+ /*
+ * Do stuff.
+ */
+
+ return 0;
+ }
+
+Now you are ready to transmit packets. > Example implementation
+``examples/common/sender.c`` and ``examples/common/responder.c``
+
+To transmit or receive data you will need to use ``memif_buffer``
+struct. The important fields here are ``void *data``, ``uint32_t len``
+and ``uint8_t flags``. The ``data`` pointer points directly to the
+shared memory packet buffer. This is where you will find/insert your
+packets. The ``len`` field is the length of the buffer. If the flag
+``MEMIF_BUFFER_FLAG_NEXT`` is present in ``flags`` field, this buffer is
+chained so the rest of the data is located in the next buffer, and so
+on.
+
+First let’s receive data. To receive data call ``memif_rx_burst()``. The
+function will fill out memif buffers passed to it. Then you would
+process your data (e.g. copy to your stack). Last you must refill the
+queue using ``memif_refill_queue()`` to notify peer that the buffers are
+now free and can be overwritten.
+
+.. code:: c
+
+ /* Fill out memif buffers and mark them as received */
+ err = memif_rx_burst(conn, qid, buffers, num_buffers, &num_received);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO ("memif_rx_burst: %s", memif_strerror(err));
+ return err;
+ }
+ /*
+ Process the buffers.
+ */
+
+ /* Refill the queue, so that the peer interface can transmit more packets */
+ err = memif_refill_queue(conn, qid, num_received, 0);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_refill_queue: %s", memif_strerror(err));
+ goto error;
+ }
+
+In order to transmit data you first need to ‘allocate’ memif buffers
+using ``memif_buffer_alloc()``. This function similar to
+``memif_rx_burst`` will fill out provided memif buffers. You will then
+insert your packets directly into the shared memory (don’t forget to
+update ``len`` filed if your packet is smaller that buffer length).
+Finally call ``memif_tx_burst`` to transmit the buffers.
+
+.. code:: c
+
+ /* Alocate memif buffers */
+ err = memif_buffer_alloc(conn, qid, buffers, num_pkts, &num_allocated, packet_size);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_buffer_alloc: %s", memif_strerror(err));
+ goto error;
+ }
+
+ /*
+ Fill out the buffers.
+
+ tx_buffers[i].data field points to the shared memory.
+ update tx_buffers[i].len to your packet length, if the packet is smaller.
+ */
+
+ /* Transmit the buffers */
+ err = memif_tx_burst(conn, qid, buffers, num_allocated, &num_transmitted);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_tx_burst: %s", memif_strerror(err));
+ goto error;
+ }
+
+Zero-copy Slave
+---------------
+
+Interface with slave role is the buffer producer, as such it can use
+zero-copy mode.
+
+After receiving buffers, process your packets in place. Then use
+``memif_buffer_enq_tx()`` to enqueue rx buffers to tx queue (by swapping
+rx buffer with a free tx buffer).
+
+.. code:: c
+
+ /* Fill out memif buffers and mark them as received */
+ err = memif_rx_burst(conn, qid, buffers, num_buffers, &num_received);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO ("memif_rx_burst: %s", memif_strerror(err));
+ return err;
+ }
+
+ /*
+ Process the buffers in place.
+ */
+
+ /* Enqueue processed buffers to tx queue */
+ err = memif_buffer_enq_tx(conn, qid, buffers, num_buffers, &num_enqueued);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_buffer_alloc: %s", memif_strerror(err));
+ goto error;
+ }
+
+ /* Refill the queue, so that the peer interface can transmit more packets */
+ err = memif_refill_queue(conn, qid, num_enqueued, 0);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_refill_queue: %s", memif_strerror(err));
+ goto error;
+ }
+
+ /* Transmit the buffers. */
+ err = memif_tx_burst(conn, qid, buffers, num_enqueued, &num_transmitted);
+ if (err != MEMIF_ERR_SUCCESS) {
+ INFO("memif_tx_burst: %s", memif_strerror(err));
+ goto error;
+ }
+
+Custom Event Polling
+--------------------
+
+Libmemif can be integrated into your applications fd event polling. You
+will need to implement ``memif_control_fd_update_t`` callback and pass
+it to ``memif_socket_args.on_control_fd_update``. Now each time any file
+descriptor belonging to that socket updates, ``on_control_fd_update``
+callback is called. The file descriptor and event type is passed in
+``memif_fd_event_t``. It also contains private context that is
+associated with this fd. When event is polled on the fd you need to call
+``memif_control_fd_handler`` and pass the event type and private context
+associated with the fd.
+
+Multi Threading
+---------------
+
+Connection establishment
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Memif sockets should not be handled in parallel. Instead each thread
+should have it’s own socket. However the UNIX socket can be the same. In
+case of non-listener socket, it’s straight forward, just create the
+socket using the same path. In case of listener socket, the polling
+should be done by single thread. > The socket becomes listener once a
+Master interface is assigned to it.
+
+Packet handling
+~~~~~~~~~~~~~~~
+
+Single queue must not be handled in parallel. Instead you can assign
+queues to threads in such way that each queue is only assigned single
+thread.
+
+Shared Memory Layout
+--------------------
+
+Please refer to `DPDK MEMIF
+documentation <http://doc.dpdk.org/guides/nics/memif.html>`__
+``'Shared memory'`` section.
diff --git a/extras/libmemif/examples/CMakeLists.txt b/extras/libmemif/examples/CMakeLists.txt
index 4f120e59f75..7622909e7be 100644
--- a/extras/libmemif/examples/CMakeLists.txt
+++ b/extras/libmemif/examples/CMakeLists.txt
@@ -13,24 +13,26 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
-set(HEADERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/icmp_responder)
+set(COMMON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/common)
set(COMMON_SOURCE_FILES
- icmp_responder/icmp_proto.c)
+ common/common.c
+ common/sender.c
+ common/responder.c
+ common/packet_handler.c
+ common/icmp_proto.c
+)
list(APPEND EXAMPLES_LIST
+ loopback/main.c
icmp_responder/main.c
- icmp_responder-epoll/main.c
- icmp_responder-mt/main.c
- icmp_responder-mt_3-1/main.c
- icmp_responder-eb/main.c
- icmp_responder-zero-copy-slave/main.c
+ test_app/main.c
)
foreach (EXAMPLE_SRC ${EXAMPLES_LIST})
string(FIND ${EXAMPLE_SRC} "/" INDEX)
string(SUBSTRING ${EXAMPLE_SRC} 0 ${INDEX} EXECUTABLE)
- add_executable(${EXECUTABLE} ${COMMON_SOURCE_FILES} ${EXAMPLE_SRC})
- target_include_directories(${EXECUTABLE} PRIVATE $<BUILD_INTERFACE:${HEADERS_DIR}>)
+ add_executable(${EXECUTABLE} ${COMMON_SOURCE_FILES} ${EXAMPLE_SRC} )
+ target_include_directories(${EXECUTABLE} PRIVATE $<BUILD_INTERFACE:${COMMON_HEADERS}>)
target_link_libraries(${EXECUTABLE} memif ${CMAKE_THREAD_LIBS_INIT})
endforeach()
diff --git a/extras/libmemif/examples/common/common.c b/extras/libmemif/examples/common/common.c
new file mode 100644
index 00000000000..5af42eaf63b
--- /dev/null
+++ b/extras/libmemif/examples/common/common.c
@@ -0,0 +1,192 @@
+#include <common.h>
+
+void
+print_memif_ring_details (memif_connection_t *c, uint16_t qid, uint8_t is_rx)
+{
+ /* TODO: print memif shared memory details */
+}
+
+void
+print_memif_rx_ring_details (memif_connection_t *c, uint16_t qid)
+{
+ print_memif_ring_details (c, qid, /* RX */ 1);
+}
+
+void
+print_memif_tx_ring_details (memif_connection_t *c, uint16_t qid)
+{
+ print_memif_ring_details (c, qid, /* TX */ 0);
+}
+
+void
+print_version ()
+{
+ printf ("libmemif version: %s, memif version: %s\n", LIBMEMIF_VERSION,
+ memif_get_version_str ());
+}
+
+int
+parse_ip4 (const char *input, uint8_t out[4])
+{
+ char *ui, *end;
+ char *tmp = strdup (input);
+
+ ui = strtok (tmp, ".");
+ if (ui == NULL)
+ return -1;
+ out[0] = strtol (ui, &end, 10);
+
+ ui = strtok (NULL, ".");
+ if (ui == NULL)
+ return -1;
+ out[1] = strtol (ui, &end, 10);
+
+ ui = strtok (NULL, ".");
+ if (ui == NULL)
+ return -1;
+ out[2] = strtol (ui, &end, 10);
+
+ ui = strtok (NULL, ".");
+ if (ui == NULL)
+ return -1;
+ out[3] = strtol (ui, &end, 10);
+
+ free (tmp);
+
+ return 0;
+}
+
+int
+parse_mac (const char *input, uint8_t out[6])
+{
+ char *ui, *end;
+ char *tmp = strdup (input);
+
+ ui = strtok (tmp, ":");
+ if (ui == NULL)
+ return -1;
+ out[0] = strtol (ui, &end, 16);
+ ui = strtok (NULL, ":");
+ if (ui == NULL)
+ return -1;
+ out[1] = strtol (ui, &end, 16);
+ ui = strtok (NULL, ":");
+ if (ui == NULL)
+ return -1;
+ out[2] = strtol (ui, &end, 16);
+ ui = strtok (NULL, ":");
+ if (ui == NULL)
+ return -1;
+ out[3] = strtol (ui, &end, 16);
+ ui = strtok (NULL, ":");
+ if (ui == NULL)
+ return -1;
+ out[4] = strtol (ui, &end, 16);
+ ui = strtok (NULL, ":");
+ if (ui == NULL)
+ return -1;
+ out[5] = strtol (ui, &end, 16);
+
+ free (tmp);
+
+ return 0;
+}
+
+void
+alloc_memif_buffers (memif_connection_t *c)
+{
+ c->rx_bufs =
+ (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
+ c->rx_buf_num = 0;
+ c->tx_bufs =
+ (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
+ c->tx_buf_num = 0;
+}
+
+void
+free_memif_buffers (memif_connection_t *c)
+{
+ if (c->rx_bufs != NULL)
+ free (c->rx_bufs);
+ c->rx_bufs = NULL;
+ c->rx_buf_num = 0;
+ if (c->tx_bufs != NULL)
+ free (c->tx_bufs);
+ c->tx_bufs = NULL;
+ c->tx_buf_num = 0;
+}
+
+void
+print_memif_details (memif_connection_t *c)
+{
+ printf ("MEMIF DETAILS\n");
+ printf ("==============================\n");
+
+ memif_details_t md;
+ memset (&md, 0, sizeof (md));
+ ssize_t buflen = 2048;
+ char *buf = (char *) malloc (buflen);
+ memset (buf, 0, buflen);
+ int err, e;
+
+ err = memif_get_details (c->conn, &md, buf, buflen);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("%s", memif_strerror (err));
+ if (err == MEMIF_ERR_NOCONN)
+ {
+ free (buf);
+ return;
+ }
+ }
+
+ printf ("\tinterface name: %s\n", (char *) md.if_name);
+ printf ("\tapp name: %s\n", (char *) md.inst_name);
+ printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
+ printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
+ printf ("\tid: %u\n", md.id);
+ printf ("\tsecret: %s\n", (char *) md.secret);
+ printf ("\trole: ");
+ if (md.role)
+ printf ("slave\n");
+ else
+ printf ("master\n");
+ printf ("\tmode: ");
+ switch (md.mode)
+ {
+ case 0:
+ printf ("ethernet\n");
+ break;
+ case 1:
+ printf ("ip\n");
+ break;
+ case 2:
+ printf ("punt/inject\n");
+ break;
+ default:
+ printf ("unknown\n");
+ break;
+ }
+ printf ("\tsocket path: %s\n", (char *) md.socket_path);
+ printf ("\trx queues:\n");
+ for (e = 0; e < md.rx_queues_num; e++)
+ {
+ printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid);
+ printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
+ printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
+ }
+ printf ("\ttx queues:\n");
+ for (e = 0; e < md.tx_queues_num; e++)
+ {
+ printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid);
+ printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
+ printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
+ }
+ printf ("\tlink: ");
+ if (md.link_up_down)
+ printf ("up\n");
+ else
+ printf ("down\n");
+
+ free (buf);
+}
diff --git a/extras/libmemif/examples/common/common.h b/extras/libmemif/examples/common/common.h
new file mode 100644
index 00000000000..f6c5edc3b4e
--- /dev/null
+++ b/extras/libmemif/examples/common/common.h
@@ -0,0 +1,120 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2020 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 _COMMON_H_
+#define _COMMON_H_
+
+#include <libmemif.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef ICMP_DBG
+#define DBG(...) \
+ do \
+ { \
+ printf (APP_NAME ":%s:%d: ", __func__, __LINE__); \
+ printf (__VA_ARGS__); \
+ printf ("\n"); \
+ } \
+ while (0)
+#else
+#define DBG(...)
+#endif
+
+#define INFO(...) \
+ do \
+ { \
+ printf ("INFO: " __VA_ARGS__); \
+ printf ("\n"); \
+ } \
+ while (0)
+
+/* maximum tx/rx memif buffers */
+#define MAX_MEMIF_BUFS 1024
+
+struct memif_connection;
+
+typedef int (memif_packet_handler_t) (struct memif_connection *conn);
+
+typedef int (packet_generator_t) (struct memif_connection *c,
+ uint16_t num_pkts);
+
+typedef struct memif_connection
+{
+ uint16_t index;
+ /* memif conenction handle */
+ memif_conn_handle_t conn;
+ uint8_t is_connected;
+ /* transmit queue id */
+ uint16_t tx_qid;
+ /* tx buffers */
+ memif_buffer_t *tx_bufs;
+ /* allocated tx buffers counter */
+ /* number of tx buffers pointing to shared memory */
+ uint16_t tx_buf_num;
+ /* rx buffers */
+ memif_buffer_t *rx_bufs;
+ /* allcoated rx buffers counter */
+ /* number of rx buffers pointing to shared memory */
+ uint16_t rx_buf_num;
+ memif_packet_handler_t *packet_handler;
+ /* interface ip address */
+ uint8_t ip_addr[4];
+ /* interface hw address */
+ uint8_t hw_addr[6];
+ /* buffer size */
+ uint16_t buffer_size;
+ /* headroom size */
+ uint16_t headroom_size;
+} memif_connection_t;
+
+void print_version ();
+
+int parse_ip4 (const char *input, uint8_t out[4]);
+
+int parse_mac (const char *input, uint8_t out[6]);
+
+void alloc_memif_buffers (memif_connection_t *c);
+
+void free_memif_buffers (memif_connection_t *c);
+
+void print_memif_details (memif_connection_t *c);
+
+void print_memif_rx_ring_details (memif_connection_t *c, uint16_t qid);
+
+void print_memif_tx_ring_details (memif_connection_t *c, uint16_t qid);
+
+int send_packets (memif_connection_t *conn, uint16_t qid,
+ packet_generator_t *gen, uint32_t num_pkts,
+ uint16_t max_pkt_size);
+
+/* Expect packets smaller than 2048b */
+int responder (memif_conn_handle_t conn, void *private_ctx, uint16_t qid);
+
+/* Expect packets smaller than 2048b */
+int responder_zero_copy (memif_conn_handle_t conn, void *private_ctx,
+ uint16_t qid);
+
+/* reply with the same data */
+int basic_packet_handler (memif_connection_t *conn);
+
+/* ICMPv4 and ARP handler */
+int icmp_packet_handler (memif_connection_t *conn);
+
+#endif /* COMMON_H */
diff --git a/extras/libmemif/examples/icmp_responder/icmp_proto.c b/extras/libmemif/examples/common/icmp_proto.c
index 785aebd5567..70825eafdb2 100644
--- a/extras/libmemif/examples/icmp_responder/icmp_proto.c
+++ b/extras/libmemif/examples/common/icmp_proto.c
@@ -83,22 +83,22 @@ print_packet (void *pck)
icmp = (struct icmphdr *) (pck + sizeof (struct iphdr));
printf ("received packet:\n");
printf ("\tiphdr:\n");
- printf ("\t\tihl: %u\n\t\tversion: %u\n\t\tlen: %u\n\t\tid: %u\n",
- ip->ihl, ip->version, __bswap_16 (ip->tot_len), ip->id);
+ printf ("\t\tihl: %u\n\t\tversion: %u\n\t\tlen: %u\n\t\tid: %u\n", ip->ihl,
+ ip->version, __bswap_16 (ip->tot_len), ip->id);
printf ("\t\tprotocol: %u\n", ip->protocol);
printf ("\t\tsaddr: ");
int i;
for (i = 0; i < 4; i++)
{
- printf ("%u.", ((uint8_t *) & ip->saddr)[i]);
+ printf ("%u.", ((uint8_t *) &ip->saddr)[i]);
}
printf ("\n");
printf ("\t\tdaddr: ");
for (i = 0; i < 4; i++)
{
- printf ("%u.", ((uint8_t *) & ip->daddr)[i]);
+ printf ("%u.", ((uint8_t *) &ip->daddr)[i]);
}
printf ("\n");
printf ("\ticmphdr:\n");
@@ -136,11 +136,11 @@ resolve_eth_arp (struct ether_arp *eth_arp, void *eth_arp_resp,
memcpy (resp->arp_tha, eth_arp->arp_sha, 6);
memcpy (resp->arp_tpa, eth_arp->arp_spa, 4);
- memcpy (resp->arp_sha,
- (((struct ether_header *) (eth_arp_resp -
- sizeof (struct
- ether_header)))->ether_shost),
- 6);
+ memcpy (
+ resp->arp_sha,
+ (((struct ether_header *) (eth_arp_resp - sizeof (struct ether_header)))
+ ->ether_shost),
+ 6);
memcpy (resp->arp_spa, ip_addr, 4);
@@ -148,17 +148,11 @@ resolve_eth_arp (struct ether_arp *eth_arp, void *eth_arp_resp,
}
static ssize_t
-resolve_eth (struct ether_header *eth, void *eth_resp)
+resolve_eth (struct ether_header *eth, void *eth_resp, uint8_t hw_addr[6])
{
struct ether_header *resp = (struct ether_header *) eth_resp;
memcpy (resp->ether_dhost, eth->ether_shost, 6);
- uint8_t hw_addr[6];
- int i;
- for (i = 0; i < 6; i++)
- {
- hw_addr[i] = 'a';
- }
memcpy (resp->ether_shost, hw_addr, 6);
resp->ether_type = eth->ether_type;
@@ -179,10 +173,10 @@ resolve_ip (struct iphdr *ip, void *ip_resp, uint8_t ip_addr[4])
resp->frag_off = 0;
resp->ttl = 0x40;
resp->protocol = 1;
- ((uint8_t *) & resp->saddr)[0] = ip_addr[0];
- ((uint8_t *) & resp->saddr)[1] = ip_addr[1];
- ((uint8_t *) & resp->saddr)[2] = ip_addr[2];
- ((uint8_t *) & resp->saddr)[3] = ip_addr[3];
+ ((uint8_t *) &resp->saddr)[0] = ip_addr[0];
+ ((uint8_t *) &resp->saddr)[1] = ip_addr[1];
+ ((uint8_t *) &resp->saddr)[2] = ip_addr[2];
+ ((uint8_t *) &resp->saddr)[3] = ip_addr[3];
resp->daddr = ip->saddr;
/* resp->check = cksum (resp, sizeof (struct iphdr)); */
@@ -204,56 +198,6 @@ resolve_icmp (struct icmphdr *icmp, void *icmp_resp)
return sizeof (struct icmphdr);
}
-int
-resolve_packet (void *in_pck, ssize_t in_size,
- void *out_pck, uint32_t * out_size, uint8_t ip_addr[4])
-{
- struct ether_header *eh;
- struct ether_arp *eah;
- struct iphdr *ip, *ip_out;
- struct icmphdr *icmp;
- *out_size = 0;
-
- if ((in_pck == NULL) || (out_pck == NULL))
- return -1;
-
- eh = (struct ether_header *) in_pck;
- *out_size = resolve_eth (eh, out_pck);
-
- if (eh->ether_type == 0x0608)
- {
- eah = (struct ether_arp *) (in_pck + *out_size);
- *out_size += resolve_eth_arp (eah, out_pck + *out_size, ip_addr);
-
- }
- else if (eh->ether_type == 0x0008)
- {
-#ifdef ICMP_DBG
- print_packet (in_pck + *out_size);
-#endif
- ip = (struct iphdr *) (in_pck + *out_size);
- ip_out = (struct iphdr *) (out_pck + *out_size);
- *out_size += resolve_ip (ip, out_pck + *out_size, ip_addr);
- if (ip->protocol == 1)
- {
- icmp = (struct icmphdr *) (in_pck + *out_size);
- *out_size += resolve_icmp (icmp, out_pck + *out_size);
- ((struct icmphdr *) (out_pck + *out_size -
- sizeof (struct icmphdr)))->checksum =
- cksum (out_pck + *out_size - sizeof (struct icmphdr),
- sizeof (struct icmphdr));
- /* payload */
- memcpy (out_pck + *out_size, in_pck + *out_size,
- in_size - *out_size);
- *out_size = in_size;
- ip_out->tot_len =
- __bswap_16 (*out_size - sizeof (struct ether_header));
- ip_out->check = cksum (ip_out, sizeof (struct iphdr));
- }
- }
- return 0;
-}
-
static ssize_t
generate_eth (struct ether_header *eh, uint8_t hw_daddr[6])
{
@@ -284,15 +228,15 @@ generate_ip (struct iphdr *ip, uint8_t saddr[4], uint8_t daddr[4])
ip->ttl = 0x40;
ip->protocol = 1;
/* saddr */
- ((uint8_t *) & ip->saddr)[0] = saddr[0];
- ((uint8_t *) & ip->saddr)[1] = saddr[1];
- ((uint8_t *) & ip->saddr)[2] = saddr[2];
- ((uint8_t *) & ip->saddr)[3] = saddr[3];
+ ((uint8_t *) &ip->saddr)[0] = saddr[0];
+ ((uint8_t *) &ip->saddr)[1] = saddr[1];
+ ((uint8_t *) &ip->saddr)[2] = saddr[2];
+ ((uint8_t *) &ip->saddr)[3] = saddr[3];
/* daddr */
- ((uint8_t *) & ip->daddr)[0] = daddr[0];
- ((uint8_t *) & ip->daddr)[1] = daddr[1];
- ((uint8_t *) & ip->daddr)[2] = daddr[2];
- ((uint8_t *) & ip->daddr)[3] = daddr[3];
+ ((uint8_t *) &ip->daddr)[0] = daddr[0];
+ ((uint8_t *) &ip->daddr)[1] = daddr[1];
+ ((uint8_t *) &ip->daddr)[2] = daddr[2];
+ ((uint8_t *) &ip->daddr)[3] = daddr[3];
ip->check = cksum (ip, sizeof (struct iphdr));
@@ -311,8 +255,8 @@ generate_icmp (struct icmphdr *icmp, uint32_t seq)
}
int
-generate_packet (void *pck, uint32_t * size, uint8_t saddr[4],
- uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq)
+generate_packet (void *pck, uint32_t *size, uint8_t saddr[4], uint8_t daddr[4],
+ uint8_t hw_daddr[6], uint32_t seq)
{
struct ether_header *eh;
struct iphdr *ip;
@@ -340,7 +284,7 @@ generate_packet (void *pck, uint32_t * size, uint8_t saddr[4],
}
int
-generate_packet2 (void *pck, uint32_t * size, uint8_t saddr[4],
+generate_packet2 (void *pck, uint32_t *size, uint8_t saddr[4],
uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq,
icmpr_flow_mode_t mode)
{
@@ -372,13 +316,17 @@ generate_packet2 (void *pck, uint32_t * size, uint8_t saddr[4],
return 0;
}
-#define GET_HEADER(out,hdr,src,off) do { \
- out = (hdr*)(src + off); \
- off += sizeof (hdr); \
- } while (0)
+#define GET_HEADER(out, hdr, src, off) \
+ do \
+ { \
+ out = (hdr *) (src + off); \
+ off += sizeof (hdr); \
+ } \
+ while (0)
int
-resolve_packet2 (void *pck, uint32_t * size, uint8_t ip_addr[4])
+resolve_packet (void *pck, uint32_t *size, uint8_t ip_addr[4],
+ uint8_t hw_addr[6])
{
struct ether_header *eh;
struct ether_arp *eah;
@@ -389,10 +337,14 @@ resolve_packet2 (void *pck, uint32_t * size, uint8_t ip_addr[4])
if (pck == NULL)
return 0;
+#ifdef ICMP_DBG
+ print_packet (pck);
+#endif
+
GET_HEADER (eh, struct ether_header, pck, offset);
memcpy (eh->ether_dhost, eh->ether_shost, 6);
- memcpy (eh->ether_shost, "aaaaaa", 6);
+ memcpy (eh->ether_shost, hw_addr, 6);
if (eh->ether_type == 0x0608)
{
@@ -430,10 +382,10 @@ resolve_packet2 (void *pck, uint32_t * size, uint8_t ip_addr[4])
ip->protocol = 1;
ip->check = 0x0000;
ip->daddr = ip->saddr;
- ((uint8_t *) & ip->saddr)[0] = ip_addr[0];
- ((uint8_t *) & ip->saddr)[1] = ip_addr[1];
- ((uint8_t *) & ip->saddr)[2] = ip_addr[2];
- ((uint8_t *) & ip->saddr)[3] = ip_addr[3];
+ ((uint8_t *) &ip->saddr)[0] = ip_addr[0];
+ ((uint8_t *) &ip->saddr)[1] = ip_addr[1];
+ ((uint8_t *) &ip->saddr)[2] = ip_addr[2];
+ ((uint8_t *) &ip->saddr)[3] = ip_addr[3];
GET_HEADER (icmp, struct icmphdr, pck, offset);
@@ -453,9 +405,8 @@ resolve_packet2 (void *pck, uint32_t * size, uint8_t ip_addr[4])
return 0;
}
-
int
-resolve_packet3 (void **pck_, uint32_t * size, uint8_t ip_addr[4])
+resolve_packet_with_encap (void **pck_, uint32_t *size, uint8_t ip_addr[4])
{
struct ether_header *eh;
struct iphdr *ip;
@@ -493,10 +444,10 @@ resolve_packet3 (void **pck_, uint32_t * size, uint8_t ip_addr[4])
ip->protocol = 1;
ip->check = 0x0000;
ip->daddr = ip->saddr;
- ((uint8_t *) & ip->saddr)[0] = ip_addr[0];
- ((uint8_t *) & ip->saddr)[1] = ip_addr[1];
- ((uint8_t *) & ip->saddr)[2] = ip_addr[2];
- ((uint8_t *) & ip->saddr)[3] = ip_addr[3];
+ ((uint8_t *) &ip->saddr)[0] = ip_addr[0];
+ ((uint8_t *) &ip->saddr)[1] = ip_addr[1];
+ ((uint8_t *) &ip->saddr)[2] = ip_addr[2];
+ ((uint8_t *) &ip->saddr)[3] = ip_addr[3];
GET_HEADER (icmp, struct icmphdr, pck, offset);
diff --git a/extras/libmemif/examples/icmp_responder/icmp_proto.h b/extras/libmemif/examples/common/icmp_proto.h
index 1575719bd6a..1371146cd40 100644
--- a/extras/libmemif/examples/icmp_responder/icmp_proto.h
+++ b/extras/libmemif/examples/common/icmp_proto.h
@@ -24,19 +24,17 @@ typedef enum
ICMPR_FLOW_MODE_IP,
} icmpr_flow_mode_t;
-int resolve_packet (void *in_pck, ssize_t in_size, void *out_pck,
- uint32_t * out_size, uint8_t ip_addr[4]);
-
/* resolve packet in place */
-int resolve_packet2 (void *pck, uint32_t * size, uint8_t ip_addr[4]);
+int resolve_packet (void *pck, uint32_t *size, uint8_t ip_addr[4],
+ uint8_t hw_addr[6]);
/* resolve packet in place and add eth encap */
-int resolve_packet3 (void **pck, uint32_t * size, uint8_t ip_addr[4]);
+int resolve_packet_with_encap (void **pck, uint32_t *size, uint8_t ip_addr[4]);
-int generate_packet (void *pck, uint32_t * size, uint8_t saddr[4],
+int generate_packet (void *pck, uint32_t *size, uint8_t saddr[4],
uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq);
-int generate_packet2 (void *pck, uint32_t * size, uint8_t saddr[4],
+int generate_packet2 (void *pck, uint32_t *size, uint8_t saddr[4],
uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq,
icmpr_flow_mode_t);
diff --git a/extras/libmemif/examples/common/packet_handler.c b/extras/libmemif/examples/common/packet_handler.c
new file mode 100644
index 00000000000..1e97e46dba2
--- /dev/null
+++ b/extras/libmemif/examples/common/packet_handler.c
@@ -0,0 +1,85 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2020 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 <common.h>
+#include <icmp_proto.h>
+
+/* reply with the same data */
+int
+basic_packet_handler (memif_connection_t *c)
+{
+ int i;
+ memif_buffer_t *dest, *src;
+
+ /* in case of zero-copy the tx_buf_num will be zero, so the loop body won't
+ * execute */
+ for (i = 0; i < c->tx_buf_num; i++)
+ {
+ memcpy (c->tx_bufs[i].data, c->rx_bufs[i].data, c->rx_bufs[i].len);
+ }
+
+ return 0;
+}
+
+/* ICMPv4 and ARP handler */
+int
+icmp_packet_handler (memif_connection_t *c)
+{
+ int i;
+ memif_buffer_t *dest, *src;
+
+ /* if tx_buf_num > 0 we use non-zero-copy mode */
+ if (c->tx_buf_num > 0)
+ {
+ for (i = 0; i < c->tx_buf_num; i++)
+ {
+ uint32_t len;
+ void *packet = c->tx_bufs[i].data;
+
+ memcpy (c->tx_bufs[i].data, c->rx_bufs[i].data, c->rx_bufs[i].len);
+ c->tx_bufs[i].flags = c->rx_bufs[i].flags;
+ len = c->tx_bufs[i].len = c->rx_bufs[i].len;
+
+ while (c->rx_bufs[i].flags & MEMIF_BUFFER_FLAG_NEXT)
+ {
+ i++;
+ memcpy (c->tx_bufs[i].data, c->rx_bufs[i].data,
+ c->rx_bufs[i].len);
+ c->tx_bufs[i].flags = c->rx_bufs[i].flags;
+ len += c->tx_bufs[i].len = c->rx_bufs[i].len;
+ }
+
+ resolve_packet (packet, &len, c->ip_addr, c->hw_addr);
+ }
+ }
+ else
+ {
+ for (i = 0; i < c->rx_buf_num; i++)
+ {
+ uint32_t len = c->rx_bufs[i].len;
+ void *packet = c->rx_bufs[i].data;
+ while (c->rx_bufs[i].flags & MEMIF_BUFFER_FLAG_NEXT)
+ {
+ i++;
+ len += c->rx_bufs[i].len;
+ }
+ resolve_packet (packet, &len, c->ip_addr, c->hw_addr);
+ }
+ }
+
+ return 0;
+}
diff --git a/extras/libmemif/examples/common/responder.c b/extras/libmemif/examples/common/responder.c
new file mode 100644
index 00000000000..3edf91125dd
--- /dev/null
+++ b/extras/libmemif/examples/common/responder.c
@@ -0,0 +1,172 @@
+#include <common.h>
+
+int
+responder (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
+{
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+ int err, i;
+ uint16_t tx;
+
+ /* receive packets from the shared memory */
+ err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_rx_burst: %s", memif_strerror (err));
+ return err;
+ }
+
+ do
+ {
+ /* allocate tx buffers */
+ err = memif_buffer_alloc (conn, qid, c->tx_bufs, c->rx_buf_num,
+ &c->tx_buf_num, c->buffer_size);
+ /* suppress full ring error MEMIF_ERR_NOBUF_RING */
+ if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING)
+ {
+ INFO ("memif_buffer_alloc: %s", memif_strerror (err));
+ goto error;
+ }
+
+ /* Process the packets */
+ if (c->packet_handler == NULL)
+ {
+ INFO ("Missing packet handler");
+ goto error;
+ }
+ err = c->packet_handler (c);
+ if (err != 0)
+ {
+ INFO ("packet handler error: %d", err);
+ goto error;
+ }
+ /* Done processing packets */
+
+ /* refill the queue */
+ err = memif_refill_queue (conn, qid, c->tx_buf_num, c->headroom_size);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ goto error;
+ }
+ c->rx_buf_num -= c->tx_buf_num;
+
+ err = memif_tx_burst (conn, qid, c->tx_bufs, c->tx_buf_num, &tx);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_tx_burst: %s", memif_strerror (err));
+ goto error;
+ }
+ c->tx_buf_num -= tx;
+
+ /* This should never happen */
+ if (c->tx_buf_num != 0)
+ {
+ INFO ("memif_tx_burst failed to send all allocated buffers.");
+ goto error;
+ }
+ }
+ while (c->rx_buf_num > 0);
+
+ return 0;
+
+error:
+ err = memif_refill_queue (conn, qid, c->rx_buf_num, c->headroom_size);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ return err;
+ }
+ c->rx_buf_num = 0;
+
+ return -1;
+}
+
+int
+responder_zero_copy (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
+{
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+ int err, i;
+ uint16_t tx, tx2;
+
+ /* receive packets from the shared memory */
+ err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_rx_burst: %s", memif_strerror (err));
+ return err;
+ }
+
+ do
+ {
+ /* Note that in zero copy memif_buffer_alloc is not part of respond
+ process,
+ * instead rx buffers are used directly using memif_buffer_enq_tx.
+ * /
+
+ /* Process the packets */
+ if (c->packet_handler == NULL)
+ {
+ INFO ("Missing packet handler");
+ goto error;
+ }
+ err = c->packet_handler (c);
+ if (err != 0)
+ {
+ INFO ("packet handler error: %d", err);
+ goto error;
+ }
+ /* Done processing packets */
+
+ /* Swap rx and tx buffers, swapped tx buffers are considered allocated
+ * and are ready to be transmitted. Notice that the buffers are swapped
+ * only in memif driver and locally remain in rx_bufs queue.
+ */
+ err = memif_buffer_enq_tx (conn, qid, c->rx_bufs, c->rx_buf_num, &tx);
+ /* suppress full ring error MEMIF_ERR_NOBUF_RING */
+ if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING)
+ {
+ INFO ("memif_buffer_alloc: %s", memif_strerror (err));
+ goto error;
+ }
+
+ /* refill the queue */
+ err = memif_refill_queue (conn, qid, tx, c->headroom_size);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ goto error;
+ }
+ c->rx_buf_num -= tx;
+
+ /* Notice that we send from rx_bufs as the buffers were only swapped
+ * internally in memif driver */
+ err = memif_tx_burst (conn, qid, c->rx_bufs, tx, &tx2);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_tx_burst: %s", memif_strerror (err));
+ goto error;
+ }
+ tx -= tx2;
+
+ /* This should never happen */
+ if (tx != 0)
+ {
+ INFO ("memif_tx_burst failed to send all allocated buffers.");
+ goto error;
+ }
+ }
+ while (c->rx_buf_num > 0);
+
+ return 0;
+
+error:
+ err = memif_refill_queue (conn, qid, c->rx_buf_num, c->headroom_size);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ return err;
+ }
+ c->rx_buf_num = 0;
+
+ return -1;
+}
diff --git a/extras/libmemif/examples/common/sender.c b/extras/libmemif/examples/common/sender.c
new file mode 100644
index 00000000000..bad926f7a16
--- /dev/null
+++ b/extras/libmemif/examples/common/sender.c
@@ -0,0 +1,55 @@
+#include <common.h>
+
+int
+send_packets (memif_connection_t *c, uint16_t qid,
+ packet_generator_t *generator, uint32_t num_pkts,
+ uint16_t max_pkt_size)
+{
+ int err, i;
+ uint16_t tx;
+
+ do
+ {
+ err = memif_buffer_alloc (c->conn, qid, c->tx_bufs,
+ num_pkts > MAX_MEMIF_BUFS ? MAX_MEMIF_BUFS :
+ num_pkts,
+ &c->tx_buf_num, max_pkt_size);
+ /* suppress full ring error MEMIF_ERR_NOBUF_RING */
+ if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING)
+ {
+ INFO ("memif_buffer_alloc: %s", memif_strerror (err));
+ goto error;
+ }
+
+ /* generate packet inside allocated buffers */
+ err = generator (c, num_pkts);
+ if (err != 0)
+ {
+ INFO ("paclet generator error: %d", err);
+ goto error;
+ }
+
+ err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &tx);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_tx_burst: %s", memif_strerror (err));
+ goto error;
+ }
+ c->tx_buf_num -= tx;
+
+ /* Should never happen... */
+ if (c->tx_buf_num > 0)
+ {
+ INFO ("Failed to send allocated packets");
+ goto error;
+ }
+ num_pkts -= tx;
+ }
+ while (num_pkts > 0);
+
+ return 0;
+
+error:
+ /* TODO: free alloocated tx buffers */
+ return -1;
+} \ No newline at end of file
diff --git a/extras/libmemif/examples/example_setup_doc.md b/extras/libmemif/examples/example_setup_doc.md
deleted file mode 100644
index 136477ddebb..00000000000
--- a/extras/libmemif/examples/example_setup_doc.md
+++ /dev/null
@@ -1,207 +0,0 @@
-## Example setup {#libmemif_example_setup_doc}
-
-#### VPP-memif master icmp_responder slave
-
-> Libmemif example app(s) use memif default socket file: `/run/vpp/memif.sock`.
-
-Run VPP and icmpr-epoll example (default example when running in container).
-
-> Other examples work similar to icmpr-epoll. Brief explanation can be found in @ref libmemif_examples_doc .
-
-VPP-side config:
-```
-DBGvpp# create interface memif id 0 master
-DBGvpp# set int state memif0/0 up
-DBGvpp# set int ip address memif0/0 192.168.1.1/24
-```
-icmpr-epoll:
-```
-conn 0 0
-```
-Memif in slave mode will try to connect every 2 seconds. If connection establishment is successful, a message will show.
-```
-INFO: memif connected!
-```
-> Error messages like "unmatched interface id" are printed only in debug mode.
-
-Check connected status.
-Use show command in icmpr-epoll:
-```
-show
-MEMIF DETAILS
-==============================
-interface index: 0
- interface ip: 192.168.1.2
- interface name: memif_connection
- app name: ICMP_Responder
- remote interface name: memif0/0
- remote app name: VPP 17.10-rc0~132-g62f9cdd
- id: 0
- secret:
- role: slave
- mode: ethernet
- socket filename: /run/vpp/memif.sock
- rx queues:
- queue id: 0
- ring size: 1024
- buffer size: 2048
- tx queues:
- queue id: 0
- ring size: 1024
- buffer size: 2048
- link: up
-interface index: 1
- no connection
-
-```
-Use sh memif command in VPP:
-```
-DBGvpp# sh memif
-interface memif0/0
- remote-name "ICMP_Responder"
- remote-interface "memif_connection"
- id 0 mode ethernet file /run/vpp/memif.sock
- flags admin-up connected
- listener-fd 12 conn-fd 13
- num-s2m-rings 1 num-m2s-rings 1 buffer-size 0
- master-to-slave ring 0:
- region 0 offset 32896 ring-size 1024 int-fd 16
- head 0 tail 0 flags 0x0000 interrupts 0
- master-to-slave ring 0:
- region 0 offset 0 ring-size 1024 int-fd 15
- head 0 tail 0 flags 0x0001 interrupts 0
-```
-
-Send ping from VPP to icmpr-epoll:
-```
-DBGvpp# ping 192.168.1.2
-64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=.1888 ms
-64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=.1985 ms
-64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=.1813 ms
-64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=.1929 ms
-
-Statistics: 5 sent, 4 received, 20% packet loss
-```
-#### multiple queues VPP-memif slave icmp_responder master
-
-Run icmpr-epoll as in previous example setup.
-Run VPP with startup conf, enabling 2 worker threads.
-Example startup.conf:
-```
-unix {
- interactive
- nodaemon
- full-coredump
-}
-
-cpu {
- workers 2
-}
-```
-VPP-side config:
-```
-DBGvpp# create memif id 0 slave rx-queues 2 tx-queues 2
-DBGvpp# set int state memif0/0 up
-DBGvpp# set int ip address memif0/0 192.168.1.1/24
-```
-icmpr-epoll:
-```
-conn 0 1
-```
-When connection is established a message will print:
-```
-INFO: memif connected!
-```
-> Error messages like "unmatched interface id" are printed only in debug mode.
-
-Check connected status.
-Use show command in icmpr-epoll:
-```
-show
-MEMIF DETAILS
-==============================
-interface index: 0
- interface ip: 192.168.1.2
- interface name: memif_connection
- app name: ICMP_Responder
- remote interface name: memif0/0
- remote app name: VPP 17.10-rc0~132-g62f9cdd
- id: 0
- secret:
- role: master
- mode: ethernet
- socket filename: /run/vpp/memif.sock
- rx queues:
- queue id: 0
- ring size: 1024
- buffer size: 2048
- queue id: 1
- ring size: 1024
- buffer size: 2048
- tx queues:
- queue id: 0
- ring size: 1024
- buffer size: 2048
- queue id: 1
- ring size: 1024
- buffer size: 2048
- link: up
-interface index: 1
- no connection
-
-```
-Use sh memif command in VPP:
-```
-DBGvpp# sh memif
-interface memif0/0
- remote-name "ICMP_Responder"
- remote-interface "memif_connection"
- id 0 mode ethernet file /run/vpp/memif.sock
- flags admin-up slave connected
- listener-fd -1 conn-fd 12
- num-s2m-rings 2 num-m2s-rings 2 buffer-size 2048
- slave-to-master ring 0:
- region 0 offset 0 ring-size 1024 int-fd 14
- head 0 tail 0 flags 0x0000 interrupts 0
- slave-to-master ring 1:
- region 0 offset 32896 ring-size 1024 int-fd 15
- head 0 tail 0 flags 0x0000 interrupts 0
- slave-to-master ring 0:
- region 0 offset 65792 ring-size 1024 int-fd 16
- head 0 tail 0 flags 0x0001 interrupts 0
- slave-to-master ring 1:
- region 0 offset 98688 ring-size 1024 int-fd 17
- head 0 tail 0 flags 0x0001 interrupts 0
-
-```
-Send ping from VPP to icmpr-epoll:
-```
-DBGvpp# ping 192.168.1.2
-64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=.1439 ms
-64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=.2184 ms
-64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=.1458 ms
-64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=.1687 ms
-
-Statistics: 5 sent, 4 received, 20% packet loss
-```
-
-#### icmp_responder master icmp_responder slave
-
-> This setup creates connection between two applications using libmemif. Traffic functionality is the same as when connection to VPP. App can receive ARP/ICMP request and transmit response.
-
-Run two instances of icmpr-epoll example.
-> If not running in container, make sure folder /run/vpp/ exists before creating memif master.
-Instance 1 will be in master mode, instance 2 in slave mode.
-instance 1:
-```
-conn 0 1
-```
-instance 2:
-```
-conn 0 0
-```
-In 2 seconds, both instances should print connected! message:
-```
-INFO: memif connected!
-```
-Check peer interface names using show command.
diff --git a/extras/libmemif/examples/examples_doc.md b/extras/libmemif/examples/examples_doc.md
deleted file mode 100644
index 4422d42d44b..00000000000
--- a/extras/libmemif/examples/examples_doc.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Examples {#libmemif_examples_doc}
-
-After build, root folder will contain scripts linking binary examples with library (same name as example apps). These scripts can be executed to run example apps without installing the library. Example apps binaries can be found in _libs_ filder. To run binaries directly, make sure that libmemif library is installed.
-
-#### Run in container
-
-`ligato/libmemif-sample-service` image contains built and installed libmemf. To run different examples, override docker CMD to start container in bash:
-
-```
-# docker run -it --entrypoint=/bin/bash -i --rm --name icmp-responder --hostname icmp-responder --privileged -v "/run/vpp/:/run/vpp/" ligato/libmemif-sample-service
-```
-Current WORKDIR is set to root repository directory. Example apps can be run from this directory (a script linking binary with library), or browse to `./.libs` folder and execute binary directly.
-
-Example app | Description
-------------|------------
-@ref extras/libmemif/examples/icmp_responder | Simplest implementation. Event polling is handled by libmemif. Single memif connection in slave mode is created (id 0). Use Ctrl + C to exit app. Memif receive mode: interrupt.
-@ref extras/libmemif/examples/icmp_responder-epoll (run in container by default) | Supports multiple connections and master mode. User can create/delete connections, set ip addresses, print connection information. @ref libmemif_example_setup_doc contains instructions on basic connection use cases setups. Memif receive mode: interrupt. App provides functionality to disable interrupts for specified queue/s for testing purposes. Polling mode is not implemented in this example.
-@ref extras/libmemif/examples/icmp_responder-mt) | Multi-thread example, very similar to icmpr-epoll. Packets are handled in threads assigned to specific queues. Slave mode only. Memif receive mode: polling (memif_rx_poll function), interrupt (memif_rx_interrupt function). Receive modes differ per queue.
diff --git a/extras/libmemif/examples/examples_doc.rst b/extras/libmemif/examples/examples_doc.rst
new file mode 100644
index 00000000000..64029773378
--- /dev/null
+++ b/extras/libmemif/examples/examples_doc.rst
@@ -0,0 +1,87 @@
+.. _libmemif_examples_doc:
+
+Libmemif Examples
+=================
+
+Example source code is located in `.../vpp/extras/libmemif/examples/` directory.
+The compiled binaries are located in `.../vpp/extras/libmemif/build/examples/`.
+
+
+ICMP Responder
+--------------
+**Application Source Code**: `.../vpp/extras/libmemif/examples/icmp_responder`
+
+In this example, memif endpoint connects to an external application. The example
+application can resolve ARP and reply to ICMPv4 packets. The program will exit
+once the interface is disconnected Memif receive mode: interrupt.
+
+VPP (memif master) <--> icmp_responder app (memif slave)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+Start VPP and configure memif interface::
+
+ make run
+ ...
+ DBGvpp# create interface memif id 0 master
+ DBGvpp# set int state memif0/0 up
+ DBGvpp# set int ip address memif0/0 192.168.1.2/24
+
+Start icmp_responder example app::
+
+ ./examples/icmp_responder
+
+Memif in slave mode will try to connect every 2 seconds. If connection
+establishment is successful, the `memif connected` message will show::
+
+ INFO: memif connected!
+
+**Note**: Error messages like "unmatched interface id" are printed only in debug mode.
+
+Verify that the memif is connected on VPP side::
+
+ DBGvpp# sh memif
+ interface memif0/0
+ remote-name "ICMP_Responder"
+ remote-interface "memif_connection"
+ id 0 mode ethernet file /run/vpp/memif.sock
+ flags admin-up connected
+ listener-fd 12 conn-fd 13
+ num-s2m-rings 1 num-m2s-rings 1 buffer-size 0
+ master-to-slave ring 0:
+ region 0 offset 32896 ring-size 1024 int-fd 16
+ head 0 tail 0 flags 0x0000 interrupts 0
+ master-to-slave ring 0:
+ region 0 offset 0 ring-size 1024 int-fd 15
+ head 0 tail 0 flags 0x0001 interrupts 0
+
+Send ping from VPP to icmp_responder (Default IPv4: 192.168.1.1)::
+
+ DBGvpp# ping 192.168.1.1
+ 64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=.1888 ms
+ 64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=.1985 ms
+ 64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=.1813 ms
+ 64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=.1929 ms
+
+ Statistics: 5 sent, 4 received, 20% packet loss
+
+
+Loopback
+--------
+**Application Source Code**: `.../vpp/extras/libmemif/examples/loopback`
+
+In this example, two memif endpoints are connected to create a loopback.
+Once connected, a test packet is sent out the memif master interface to
+the memif slave interface, which replies with the same packet in a
+zero-copy way.
+In reverse mode, the packet is sent from the slave interface and is
+looped back by the master interface.
+
+Running The Loopback Application
+++++++++++++++++++++++++++++++++
+Start the loopback example::
+
+ ./examples/loopback
+
+You should see the `Received correct data.` message::
+
+ INFO: Received correct data.
+ INFO: Stopping the program
diff --git a/extras/libmemif/examples/icmp_responder-eb/main.c b/extras/libmemif/examples/icmp_responder-eb/main.c
deleted file mode 100644
index 86a49da7935..00000000000
--- a/extras/libmemif/examples/icmp_responder-eb/main.c
+++ /dev/null
@@ -1,1069 +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 <stdlib.h>
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-#include <netinet/if_ether.h>
-#include <net/if_arp.h>
-#include <asm/byteorder.h>
-#include <byteswap.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <errno.h>
-#include <unistd.h>
-#include <signal.h>
-#include <pthread.h>
-#include <time.h>
-
-#ifndef TIME_UTC
-#define TIME_UTC 1
-#endif /* TIME_UTC */
-
-#include <libmemif.h>
-#include <icmp_proto.h>
-
-#define APP_NAME "ICMP_Responder"
-#define IF_NAME "memif_connection"
-
-#ifdef ICMP_DBG
-#define DBG(...) do { \
- printf(APP_NAME":%s:%d",__func__,__LINE__); \
- printf(__VA_ARGS__); \
- printf("\n"); \
- } while (0)
-#else
-#define DBG(...)
-#endif /* ICMP_DBG */
-
-#define INFO(...) do { \
- printf("INFO: "__VA_ARGS__); \
- printf("\n"); \
- } while (0)
-
-#define MAX_MEMIF_BUFS 256
-#define MAX_CONNS 50
-
-
-#ifndef __NR_memfd_create
-#if defined __x86_64__
-#define __NR_memfd_create 319
-#elif defined __arm__
-#define __NR_memfd_create 385
-#elif defined __aarch64__
-#define __NR_memfd_create 279
-#else
-#error "__NR_memfd_create unknown for this architecture"
-#endif
-#endif
-
-#ifndef HAVE_MEMFD_CREATE
-static inline int
-memfd_create (const char *name, unsigned int flags)
-{
- return syscall (__NR_memfd_create, name, flags);
-}
-#endif
-
-#ifndef F_LINUX_SPECIFIC_BASE
-#define F_LINUX_SPECIFIC_BASE 1024
-#endif
-
-#ifndef MFD_ALLOW_SEALING
-#define MFD_ALLOW_SEALING 0x0002U
-#endif
-
-#ifndef F_ADD_SEALS
-#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
-#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
-
-#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
-#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
-#define F_SEAL_GROW 0x0004 /* prevent file from growing */
-#define F_SEAL_WRITE 0x0008 /* prevent writes */
-#endif
-
-typedef struct
-{
- uint16_t index;
-
- memif_conn_handle_t conn;
-
- uint16_t tx_buf_num;
- uint16_t rx_buf_num;
- memif_buffer_t *tx_bufs;
- memif_buffer_t *rx_bufs;
-
- uint8_t ip_addr[4];
- uint64_t tx_counter, rx_counter, tx_err_counter;
- uint64_t t_sec, t_nsec;
-} memif_connection_t;
-
-typedef struct
-{
- uint16_t index;
- uint64_t packet_num;
- uint8_t ip_daddr[4];
- uint8_t hw_daddr[6];
-} icmpr_thread_data_t;
-
-icmpr_thread_data_t icmpr_thread_data[MAX_CONNS];
-pthread_t thread[MAX_CONNS];
-int epfd;
-long ctx[MAX_CONNS];
-memif_connection_t memif_connection[MAX_CONNS];
-
-static void
-print_help ()
-{
- printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
-#ifdef ICMP_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("==============================\n");
- printf ("libmemif version: %s", LIBMEMIF_VERSION);
-#ifdef MEMIF_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("memif version: %d\n", memif_get_version ());
- printf ("commands:\n");
- printf ("\thelp - prints this help\n");
- printf ("\texit - exit app\n");
- printf
- ("\tconn <index> <mode> <interrupt> - create memif. index used as interface id. mode: 0 = slave | 1 = master. interrupt: none = default | 1 = handle ARP only\n");
- printf ("\tdel <index> - delete memif\n");
- printf ("\tshow - show connection details\n");
- printf ("\tsend <index> <tx> <ip> <mac> - send icmp\n");
-}
-
-static void
-print_memif_details ()
-{
- memif_details_t md;
- ssize_t buflen;
- char *buf;
- int err, i, e;
- buflen = 2048;
- buf = malloc (buflen);
- printf ("MEMIF DETAILS\n");
- printf ("==============================\n");
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
-
- memset (&md, 0, sizeof (md));
- memset (buf, 0, buflen);
-
- err = memif_get_details (c->conn, &md, buf, buflen);
- if (err != MEMIF_ERR_SUCCESS)
- {
- if (err != MEMIF_ERR_NOCONN)
- INFO ("%s", memif_strerror (err));
- continue;
- }
-
- printf ("interface index: %d\n", i);
-
- printf ("\tinterface ip: %u.%u.%u.%u\n",
- c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
- printf ("\tinterface name: %s\n", (char *) md.if_name);
- printf ("\tapp name: %s\n", (char *) md.inst_name);
- printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
- printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
- printf ("\tid: %u\n", md.id);
- printf ("\tsecret: %s\n", (char *) md.secret);
- printf ("\trole: ");
- if (md.role)
- printf ("slave\n");
- else
- printf ("master\n");
- printf ("\tmode: ");
- switch (md.mode)
- {
- case 0:
- printf ("ethernet\n");
- break;
- case 1:
- printf ("ip\n");
- break;
- case 2:
- printf ("punt/inject\n");
- break;
- default:
- printf ("unknown\n");
- break;
- }
- printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
- printf ("\tregions:\n");
- for (e = 0; e < md.regions_num; e++)
- {
- printf ("\t\tindex: %u\n", md.regions[e].index);
- printf ("\t\taddress: %p\n", md.regions[e].addr);
- printf ("\t\tsize: %u\n", md.regions[e].size);
- printf ("\t\tfd: %d\n", md.regions[e].fd);
- if (md.regions[e].is_external)
- printf ("\t\texternal\n");
- }
- printf ("\trx queues:\n");
- for (e = 0; e < md.rx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
- printf ("\t\tring rx mode: %s\n",
- md.rx_queues[e].flags ? "polling" : "interrupt");
- printf ("\t\tring head: %u\n", md.rx_queues[e].head);
- printf ("\t\tring tail: %u\n", md.rx_queues[e].tail);
- printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
- }
- printf ("\ttx queues:\n");
- for (e = 0; e < md.tx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
- printf ("\t\tring rx mode: %s\n",
- md.tx_queues[e].flags ? "polling" : "interrupt");
- printf ("\t\tring head: %u\n", md.tx_queues[e].head);
- printf ("\t\tring tail: %u\n", md.tx_queues[e].tail);
- printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
- }
- printf ("\tlink: ");
- if (md.link_up_down)
- printf ("up\n");
- else
- printf ("down\n");
- }
- free (buf);
-}
-
-void
-icmpr_print_counters ()
-{
- int i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn == NULL)
- continue;
- printf ("===============================\n");
- printf ("interface index: %d\n", c->index);
- printf ("\trx: %lu\n", c->rx_counter);
- printf ("\ttx: %lu\n", c->tx_counter);
- printf ("\ttx_err: %lu\n", c->tx_err_counter);
- }
-}
-
-void
-icmpr_reset_counters ()
-{
- int i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn == NULL)
- continue;
- c->tx_err_counter = c->tx_counter = c->rx_counter = 0;
- }
-}
-
-int
-add_epoll_fd (int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d added to epoll", fd);
- return 0;
-}
-
-int
-mod_epoll_fd (int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d modified on epoll", fd);
- return 0;
-}
-
-int
-del_epoll_fd (int fd)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d removed from epoll", fd);
- return 0;
-}
-
-int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif connected!");
- memif_refill_queue (conn, 0, -1, 0);
- return 0;
-}
-
-int
-on_disconnect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif disconnected!");
- return 0;
-}
-
-int
-control_fd_update (int fd, uint8_t events, void *ctx)
-{
- /* convert memif event definitions to epoll events */
- if (events & MEMIF_FD_EVENT_DEL)
- return del_epoll_fd (fd);
-
- uint32_t evt = 0;
- if (events & MEMIF_FD_EVENT_READ)
- evt |= EPOLLIN;
- if (events & MEMIF_FD_EVENT_WRITE)
- evt |= EPOLLOUT;
-
- if (events & MEMIF_FD_EVENT_MOD)
- return mod_epoll_fd (fd, evt);
-
- return add_epoll_fd (fd, evt);
-}
-
-int
-icmpr_memif_delete (long index)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- if (c->rx_bufs)
- free (c->rx_bufs);
- c->rx_bufs = NULL;
- c->rx_buf_num = 0;
- if (c->tx_bufs)
- free (c->tx_bufs);
- c->tx_bufs = NULL;
- c->tx_buf_num = 0;
-
- int err;
- /* disconnect then delete memif connection */
- err = memif_delete (&c->conn);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
- if (c->conn != NULL)
- INFO ("memif delete fail");
- return 0;
-}
-
-int
-icmpr_free ()
-{
- /* application cleanup */
- int err;
- long i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn)
- icmpr_memif_delete (i);
- }
-
- err = memif_cleanup ();
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
-
- return 0;
-}
-
-int
-icmpr_add_external_region (void * *addr, uint32_t size, int *fd,
- void *private_ctx)
-{
-
- int rfd;
- void *raddr;
- int err;
-
- rfd = memfd_create ("memif region 1", MFD_ALLOW_SEALING);
-
- fcntl (rfd, F_ADD_SEALS, F_SEAL_SHRINK);
-
- err = ftruncate (rfd, size);
-
- if (err)
- return err;
-
- raddr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, rfd, 0);
-
- *addr = raddr;
- *fd = rfd;
-
- return 0;
-}
-
-void *
-icmpr_get_external_region_addr (uint32_t size, int fd, void *private_ctx)
-{
- return mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-}
-
-int
-icmpr_del_external_region (void *addr, uint32_t size, int fd,
- void *private_ctx)
-{
- munmap (addr, size);
- if (fd > 0)
- close (fd);
- fd = -1;
- return 0;
-}
-
-int
-on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- uint16_t rx = 0, tx = 0;
- int i = 0;
- int j = 0;
-
- do
- {
- err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- c->rx_counter += rx;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
- i = 0;
- memset (c->tx_bufs, 0, sizeof (memif_buffer_t) * rx);
- err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, rx, &tx, 128);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- j = 0;
- c->tx_err_counter += rx - tx;
-
- while (tx)
- {
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + j)->data,
- &(c->tx_bufs + j)->len, c->ip_addr);
- i++;
- j++;
- tx--;
- }
-
- err = memif_refill_queue (c->conn, qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- rx -= rx;
-
- DBG ("%u/%u alloc/free buffers", rx, MAX_MEMIF_BUFS - rx);
-
- err = memif_tx_burst (c->conn, qid, c->tx_bufs, j, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->tx_counter += tx;
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
- DBG ("freed %d buffers. %u/%u alloc/free buffers", rx, c->rx_buf_num,
- MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-int
-on_interrupt1 (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- int i;
- uint16_t rx, tx;
-
- do
- {
- /* receive data from shared memory buffers */
- err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->rx_buf_num += rx;
- c->rx_counter += rx;
-
- for (i = 0; i < rx; i++)
- {
- if (((struct ether_header *) (c->rx_bufs + i)->data)->ether_type ==
- 0x0608)
- {
- err =
- memif_buffer_alloc (c->conn, qid, c->tx_bufs, 1, &tx, 128);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + i)->data,
- &(c->tx_bufs + i)->len, c->ip_addr);
- err =
- memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- c->tx_buf_num -= tx;
- c->tx_counter += tx;
- }
- }
-
- err = memif_refill_queue (c->conn, qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
-
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-int
-icmpr_memif_create (long index, long mode, char *s)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.is_master = mode;
- args.log2_ring_size = 11;
- args.buffer_size = 2048;
- args.num_s2m_rings = 1;
- args.num_m2s_rings = 1;
- strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
- args.mode = 0;
- args.interface_id = index;
-
- int err;
- if (s == NULL)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else
- {
- if (strncmp (s, "1", 1) == 0)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt1,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else
- {
- INFO ("Unknown interrupt descriptor");
- goto done;
- }
- }
-
- c->index = index;
- c->rx_buf_num = 0;
- c->rx_bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- c->tx_buf_num = 0;
- c->tx_bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
-
- c->ip_addr[0] = 192;
- c->ip_addr[1] = 168;
- c->ip_addr[2] = c->index + 1;
- c->ip_addr[3] = 2;
-
-done:
- return 0;
-}
-
-void *
-icmpr_send_proc (void *data)
-{
- icmpr_thread_data_t *d = (icmpr_thread_data_t *) data;
- int index = d->index;
- uint64_t count = d->packet_num;
- memif_connection_t *c = &memif_connection[index];
- if (c->conn == NULL)
- {
- INFO ("No connection at index %d.", index);
- goto error;
- }
- uint16_t tx, i;
- int err = MEMIF_ERR_SUCCESS;
- uint32_t seq = 0;
- struct timespec start, end;
- memset (&start, 0, sizeof (start));
- memset (&end, 0, sizeof (end));
-
- timespec_get (&start, TIME_UTC);
- while (count)
- {
- i = 0;
- err =
- memif_buffer_alloc (c->conn, 0, c->tx_bufs,
- MAX_MEMIF_BUFS > count ? count : MAX_MEMIF_BUFS,
- &tx, 128);
-
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- c->tx_buf_num += tx;
-
- while (tx)
- {
- while (tx > 4)
- {
- generate_packet ((void *) c->tx_bufs[i].data,
- &c->tx_bufs[i].len, c->ip_addr, d->ip_daddr,
- d->hw_daddr, seq++);
- generate_packet ((void *) c->tx_bufs[i + 1].data,
- &c->tx_bufs[i + 1].len, c->ip_addr,
- d->ip_daddr, d->hw_daddr, seq++);
- generate_packet ((void *) c->tx_bufs[i + 2].data,
- &c->tx_bufs[i + 2].len, c->ip_addr,
- d->ip_daddr, d->hw_daddr, seq++);
- generate_packet ((void *) c->tx_bufs[i + 3].data,
- &c->tx_bufs[i + 3].len, c->ip_addr,
- d->ip_daddr, d->hw_daddr, seq++);
- i += 4;
- tx -= 4;
- }
- generate_packet ((void *) c->tx_bufs[i].data, &c->tx_bufs[i].len,
- c->ip_addr, d->ip_daddr, d->hw_daddr, seq++);
- i++;
- tx--;
- }
-
- err = memif_tx_burst (c->conn, 0, c->tx_bufs, c->tx_buf_num, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->tx_buf_num -= tx;
- c->tx_counter += tx;
- count -= tx;
- }
-
- timespec_get (&end, TIME_UTC);
- INFO ("\n\nPacket sequence finished!\nSeq len: %u", seq);
- uint64_t t1 = end.tv_sec - start.tv_sec;
- uint64_t t2;
- if (end.tv_nsec > start.tv_nsec)
- {
- t2 = end.tv_nsec - start.tv_nsec;
- }
- else
- {
- t2 = start.tv_nsec - end.tv_nsec;
- t1--;
- }
- c->t_sec = t1;
- c->t_nsec = t2;
- double tmp = t1;
- tmp += t2 / 1e+9;
- tmp = seq / tmp;
- INFO ("Seq time: %lus %luns\nAverage pps: %f", t1, t2, tmp);
-
-error:
- INFO ("Thread exiting...");
- pthread_exit (NULL);
-}
-
-
-int
-icmpr_send (long index, long packet_num, char *hw, char *ip)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
- if (c->conn == NULL)
- return -1;
- char *end, *ui;
- uint8_t tmp[6];
- icmpr_thread_data_t *data = &icmpr_thread_data[index];
- data->index = index;
- data->packet_num = packet_num;
-
- ui = strtok (ip, ".");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 10);
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 10);
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 10);
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 10);
-
- data->ip_daddr[0] = tmp[0];
- data->ip_daddr[1] = tmp[1];
- data->ip_daddr[2] = tmp[2];
- data->ip_daddr[3] = tmp[3];
-
- ui = strtok (hw, ":");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[4] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[5] = strtol (ui, &end, 16);
-
- pthread_create (&thread[index], NULL, icmpr_send_proc, (void *) data);
- return 0;
-
-error:
- INFO ("Invalid input\n");
- return 0;
-}
-
-int
-user_input_handler ()
-{
- char *in = (char *) malloc (256);
- char *ui = fgets (in, 256, stdin);
- char *end;
- long a;
-
- if (in[0] == '\n')
- goto done;
-
- ui = strtok (in, " ");
-
- if (strncmp (ui, "exit", 4) == 0)
- {
- free (in);
- icmpr_free ();
- exit (EXIT_SUCCESS);
- }
- else if (strncmp (ui, "help", 4) == 0)
- {
- print_help ();
- goto done;
- }
- else if (strncmp (ui, "conn", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_create (a, strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected mode <0|1>");
- goto done;
- }
- else if (strncmp (ui, "del", 3) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_delete (strtol (ui, &end, 10));
- else
- {
- INFO ("expected id");
- goto done;
- }
- }
- else if (strncmp (ui, "send", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_send (a, strtol (ui, &end, 10), strtok (NULL, " "),
- strtok (NULL, " "));
- else
- INFO ("expected count");
- goto done;
- }
- else if (strncmp (ui, "show", 4) == 0)
- {
- print_memif_details ();
- goto done;
- }
-
-done:
- free (in);
- return 0;
-}
-
-int
-poll_event (int timeout)
-{
- struct epoll_event evt;
- int app_err = 0, memif_err = 0, en = 0;
- uint32_t events = 0;
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
- sigset_t sigset;
- sigemptyset (&sigset);
- en = epoll_pwait (epfd, &evt, 1, timeout, &sigset);
- /* id event polled */
- if (en < 0)
- {
- DBG ("epoll_pwait: %s", strerror (errno));
- return -1;
- }
- if (en > 0)
- {
- /* this app does not use any other file descriptors than stds and memif control fds */
- if (evt.data.fd > 2)
- {
- /* event of memif control fd */
- /* convert epoll events to memif events */
- if (evt.events & EPOLLIN)
- events |= MEMIF_FD_EVENT_READ;
- if (evt.events & EPOLLOUT)
- events |= MEMIF_FD_EVENT_WRITE;
- if (evt.events & EPOLLERR)
- events |= MEMIF_FD_EVENT_ERROR;
- memif_err = memif_control_fd_handler (evt.data.fd, events);
- if (memif_err != MEMIF_ERR_SUCCESS)
- INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err));
- }
- else if (evt.data.fd == 0)
- {
- app_err = user_input_handler ();
- }
- else
- {
- DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd);
- }
- }
-
- if ((app_err < 0) || (memif_err < 0))
- {
- if (app_err < 0)
- DBG ("user input handler error");
- if (memif_err < 0)
- DBG ("memif control fd handler error");
- return -1;
- }
-
- return 0;
-}
-
-int
-main ()
-{
- epfd = epoll_create (1);
- add_epoll_fd (0, EPOLLIN);
-
- /* initialize memory interface */
- int err, i;
- /* if valid callback is passed as argument, fd event polling will be done by user
- all file descriptors and events will be passed to user in this callback */
- /* if callback is set to NULL libmemif will handle fd event polling */
- err = memif_init (control_fd_update, APP_NAME, NULL, NULL, NULL);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_init: %s", memif_strerror (err));
- icmpr_free ();
- exit (-1);
- }
-
- for (i = 0; i < MAX_CONNS; i++)
- {
- memset (&memif_connection[i], 0, sizeof (memif_connection_t));
- ctx[i] = i;
- }
-
- memif_register_external_region (icmpr_add_external_region,
- icmpr_get_external_region_addr,
- icmpr_del_external_region, NULL);
-
- print_help ();
-
- /* main loop */
- while (1)
- {
- if (poll_event (-1) < 0)
- {
- DBG ("poll_event error!");
- }
- }
-}
diff --git a/extras/libmemif/examples/icmp_responder-epoll/main.c b/extras/libmemif/examples/icmp_responder-epoll/main.c
deleted file mode 100644
index c2ea348d897..00000000000
--- a/extras/libmemif/examples/icmp_responder-epoll/main.c
+++ /dev/null
@@ -1,1320 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 <stdlib.h>
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-#include <netinet/if_ether.h>
-#include <net/if_arp.h>
-#include <asm/byteorder.h>
-#include <byteswap.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <errno.h>
-#include <unistd.h>
-#include <signal.h>
-#include <pthread.h>
-
-#include <time.h>
-
-#ifndef TIME_UTC
-#define TIME_UTC 1
-#endif
-
-#include <libmemif.h>
-#include <icmp_proto.h>
-
-#define APP_NAME "ICMP_Responder"
-#define IF_NAME "memif_connection"
-
-
-#ifdef ICMP_DBG
-#define DBG(...) do { \
- printf (APP_NAME":%s:%d: ", __func__, __LINE__); \
- printf (__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-#define LOG(...) do { \
- if (enable_log) { \
- dprintf (out_fd, __VA_ARGS__); \
- dprintf (out_fd, "\n"); \
- } \
- } while (0)
-#define LOG_FILE "/tmp/memif_time_test.txt"
-#else
-#define DBG(...)
-#define LOG(...)
-#endif
-
-#define INFO(...) do { \
- printf ("INFO: "__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-
-
-/* maximum tx/rx memif buffers */
-#define MAX_MEMIF_BUFS 256
-#define MAX_CONNS 50
-
-#define ICMPR_HEADROOM 64
-
-int epfd;
-int out_fd;
-uint8_t enable_log;
-
-typedef struct
-{
- uint16_t index;
- /* memif connection handle */
- memif_conn_handle_t conn;
- /* tx buffers */
- memif_buffer_t *tx_bufs;
- /* allocated tx buffers counter */
- /* number of tx buffers pointing to shared memory */
- uint16_t tx_buf_num;
- /* rx buffers */
- memif_buffer_t *rx_bufs;
- /* allocated rx buffers counter */
- /* number of rx buffers pointing to shared memory */
- uint16_t rx_buf_num;
- /* interface ip address */
- uint8_t ip_addr[4];
- uint16_t seq;
- uint64_t tx_counter, rx_counter, tx_err_counter;
- uint64_t t_sec, t_nsec;
-} memif_connection_t;
-
-typedef struct
-{
- uint16_t index;
- uint64_t packet_num;
- uint8_t ip_daddr[4];
- uint8_t hw_daddr[6];
-} icmpr_thread_data_t;
-
-memif_connection_t memif_connection[MAX_CONNS];
-icmpr_thread_data_t icmpr_thread_data[MAX_CONNS];
-pthread_t thread[MAX_CONNS];
-long ctx[MAX_CONNS];
-
-/* print details for all memif connections */
-static void
-print_memif_details ()
-{
- memif_details_t md;
- ssize_t buflen;
- char *buf;
- int err, i, e;
- buflen = 2048;
- buf = malloc (buflen);
- printf ("MEMIF DETAILS\n");
- printf ("==============================\n");
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
-
- memset (&md, 0, sizeof (md));
- memset (buf, 0, buflen);
-
- err = memif_get_details (c->conn, &md, buf, buflen);
- if (err != MEMIF_ERR_SUCCESS)
- {
- if (err != MEMIF_ERR_NOCONN)
- INFO ("%s", memif_strerror (err));
- continue;
- }
-
- printf ("interface index: %d\n", i);
-
- printf ("\tinterface ip: %u.%u.%u.%u\n",
- c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
- printf ("\tinterface name: %s\n", (char *) md.if_name);
- printf ("\tapp name: %s\n", (char *) md.inst_name);
- printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
- printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
- printf ("\tid: %u\n", md.id);
- printf ("\tsecret: %s\n", (char *) md.secret);
- printf ("\trole: ");
- if (md.role)
- printf ("slave\n");
- else
- printf ("master\n");
- printf ("\tmode: ");
- switch (md.mode)
- {
- case 0:
- printf ("ethernet\n");
- break;
- case 1:
- printf ("ip\n");
- break;
- case 2:
- printf ("punt/inject\n");
- break;
- default:
- printf ("unknown\n");
- break;
- }
- printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
- printf ("\trx queues:\n");
- for (e = 0; e < md.rx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
- printf ("\t\tring rx mode: %s\n",
- md.rx_queues[e].flags ? "polling" : "interrupt");
- printf ("\t\tring head: %u\n", md.rx_queues[e].head);
- printf ("\t\tring tail: %u\n", md.rx_queues[e].tail);
- printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
- }
- printf ("\ttx queues:\n");
- for (e = 0; e < md.tx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
- printf ("\t\tring rx mode: %s\n",
- md.tx_queues[e].flags ? "polling" : "interrupt");
- printf ("\t\tring head: %u\n", md.tx_queues[e].head);
- printf ("\t\tring tail: %u\n", md.tx_queues[e].tail);
- printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
- }
- printf ("\tlink: ");
- if (md.link_up_down)
- printf ("up\n");
- else
- {
- printf ("down\n");
- printf ("\treason: %s\n", md.error);
- }
- }
- free (buf);
-}
-
-int
-add_epoll_fd (int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d added to epoll", fd);
- return 0;
-}
-
-int
-mod_epoll_fd (int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d modified on epoll", fd);
- return 0;
-}
-
-int
-del_epoll_fd (int fd)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d removed from epoll", fd);
- return 0;
-}
-
-/* informs user about connected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif connected!");
- memif_refill_queue (conn, 0, -1, ICMPR_HEADROOM);
- enable_log = 1;
- return 0;
-}
-
-/* informs user about disconnected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_disconnect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif disconnected!");
- return 0;
-}
-
-/* user needs to watch new fd or stop watching fd that is about to be closed.
- control fd will be modified during connection establishment to minimize CPU usage */
-int
-control_fd_update (int fd, uint8_t events, void *ctx)
-{
- /* convert memif event definitions to epoll events */
- if (events & MEMIF_FD_EVENT_DEL)
- return del_epoll_fd (fd);
-
- uint32_t evt = 0;
- if (events & MEMIF_FD_EVENT_READ)
- evt |= EPOLLIN;
- if (events & MEMIF_FD_EVENT_WRITE)
- evt |= EPOLLOUT;
-
- if (events & MEMIF_FD_EVENT_MOD)
- return mod_epoll_fd (fd, evt);
-
- return add_epoll_fd (fd, evt);
-}
-
-int
-icmpr_buffer_alloc (long index, long n, uint16_t * r, uint16_t i,
- uint16_t qid)
-{
- memif_connection_t *c = &memif_connection[index];
- int err;
- /* set data pointer to shared memory and set buffer_len to shared memory buffer len */
- err = memif_buffer_alloc (c->conn, qid, c->tx_bufs + i, n, r, 128);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- return -1;
- }
- c->tx_buf_num += *r;
- DBG ("allocated %d/%ld buffers, %u free buffers", *r, n,
- MAX_MEMIF_BUFS - c->tx_buf_num);
- return 0;
-}
-
-int
-icmpr_tx_burst (long index, uint16_t qid)
-{
- memif_connection_t *c = &memif_connection[index];
- int err;
- uint16_t r;
- /* inform peer memif interface about data in shared memory buffers */
- /* mark memif buffers as free */
- err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- c->tx_buf_num -= r;
- c->tx_counter += r;
- return 0;
-}
-
-/* called when event is polled on interrupt file descriptor.
- there are packets in shared memory ready to be received */
-int
-on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- uint16_t rx = 0, tx = 0;
- int i = 0; /* rx buffer iterator */
- int j = 0; /* tx buffer iterator */
-
- /* loop while there are packets in shm */
- do
- {
- /* receive data from shared memory buffers */
- err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- c->rx_counter += rx;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
-
- i = 0;
- memset (c->tx_bufs, 0, sizeof (memif_buffer_t) * rx);
- err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, rx, &tx, 128);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- j = 0;
- c->tx_err_counter += rx - tx;
-
- while (tx)
- {
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + j)->data,
- &(c->tx_bufs + j)->len, c->ip_addr);
- i++;
- j++;
- tx--;
- }
-
- err = memif_refill_queue (c->conn, qid, rx, ICMPR_HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- rx -= rx;
-
- DBG ("%u/%u alloc/free buffers", rx, MAX_MEMIF_BUFS - rx);
-
- err = memif_tx_burst (c->conn, qid, c->tx_bufs, j, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->tx_counter += tx;
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, rx, ICMPR_HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-/* called when event is polled on interrupt file descriptor.
- there are packets in shared memory ready to be received */
-/* dev test modification: loop until TX == RX (don't drop buffers) */
-int
-on_interrupt0 (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- uint16_t rx = 0, tx = 0;
- int i; /* rx buffer iterator */
- int j; /* tx bufferiterator */
-
- /* loop while there are packets in shm */
- do
- {
- /* receive data from shared memory buffers */
- err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- c->rx_counter += rx;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
-
- i = 0;
-
- /* loop while there are RX buffers to be processed */
- while (rx)
- {
-
- /* try to alloc required number of buffers buffers */
- err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, rx, &tx, 128);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- j = 0;
-
- /* process buffers */
- while (tx)
- {
- while (tx > 2)
- {
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + j)->data,
- &(c->tx_bufs + j)->len, c->ip_addr);
- resolve_packet ((void *) (c->rx_bufs + i + 1)->data,
- (c->rx_bufs + i + 1)->len,
- (void *) (c->tx_bufs + j + 1)->data,
- &(c->tx_bufs + j + 1)->len, c->ip_addr);
-
- i += 2;
- j += 2;
- tx -= 2;
- }
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + j)->data,
- &(c->tx_bufs + j)->len, c->ip_addr);
- i++;
- j++;
- tx--;
- }
- /* mark memif buffers and shared memory buffers as free */
- /* free processed buffers */
- err = memif_refill_queue (c->conn, qid, j, ICMPR_HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- rx -= j;
-
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, rx, MAX_MEMIF_BUFS - rx);
-
- /* transmit allocated buffers */
- err = memif_tx_burst (c->conn, qid, c->tx_bufs, j, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->tx_counter += tx;
-
- }
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, rx, ICMPR_HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-/* called when event is polled on interrupt file descriptor.
- there are packets in shared memory ready to be received */
-/* dev test modification: handle only ARP requests */
-int
-on_interrupt1 (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- int i;
- uint16_t rx, tx;
-
- do
- {
- /* receive data from shared memory buffers */
- err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->rx_buf_num += rx;
- c->rx_counter += rx;
-
- for (i = 0; i < rx; i++)
- {
- if (((struct ether_header *) (c->rx_bufs + i)->data)->ether_type ==
- 0x0608)
- {
- if (icmpr_buffer_alloc (c->index, 1, &tx, i, qid) < 0)
- break;
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + i)->data,
- &(c->tx_bufs + i)->len, c->ip_addr);
- icmpr_tx_burst (c->index, qid);
- }
- }
-
- err = memif_refill_queue (c->conn, qid, rx, ICMPR_HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
-
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, rx, ICMPR_HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-int
-icmpr_memif_create (long index, long mode, char *s)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- /* setting memif connection arguments */
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.is_master = mode;
- args.log2_ring_size = 11;
- args.buffer_size = 2048;
- args.num_s2m_rings = 1;
- args.num_m2s_rings = 1;
- strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
- args.mode = 0;
- /* socket filename is not specified, because this app is supposed to
- connect to VPP over memif. so default socket filename will be used */
- /* default socketfile = /run/vpp/memif.sock */
-
- args.interface_id = index;
- /* last argument for memif_create (void * private_ctx) is used by user
- to identify connection. this context is returned with callbacks */
- int err;
- /* default interrupt */
- if (s == NULL)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else
- {
- if (strncmp (s, "0", 1) == 0)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt0,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else if (strncmp (s, "1", 1) == 0)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt1,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else
- {
- INFO ("Unknown interrupt descriptor");
- goto done;
- }
- }
-
- c->index = index;
- /* alloc memif buffers */
- c->rx_buf_num = 0;
- c->rx_bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- c->tx_buf_num = 0;
- c->tx_bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
-
- c->ip_addr[0] = 192;
- c->ip_addr[1] = 168;
- c->ip_addr[2] = c->index + 1;
- c->ip_addr[3] = 2;
-
- c->seq = c->tx_err_counter = c->tx_counter = c->rx_counter = 0;
-
-done:
- return 0;
-}
-
-int
-icmpr_memif_delete (long index)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- if (c->rx_bufs)
- free (c->rx_bufs);
- c->rx_bufs = NULL;
- c->rx_buf_num = 0;
- if (c->tx_bufs)
- free (c->tx_bufs);
- c->tx_bufs = NULL;
- c->tx_buf_num = 0;
-
- int err;
- /* disconnect then delete memif connection */
- err = memif_delete (&c->conn);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
- if (c->conn != NULL)
- INFO ("memif delete fail");
- return 0;
-}
-
-void
-print_help ()
-{
- printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
-#ifdef ICMP_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("==============================\n");
- printf ("libmemif version: %s", LIBMEMIF_VERSION);
-#ifdef MEMIF_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("memif version: %d\n", memif_get_version ());
- printf ("commands:\n");
- printf ("\thelp - prints this help\n");
- printf ("\texit - exit app\n");
- printf
- ("\tconn <index> <mode> [<interrupt-desc>] - create memif. index is also used as interface id, mode 0 = slave 1 = master, interrupt-desc none = default 0 = if ring is full wait 1 = handle only ARP requests\n");
- printf ("\tdel <index> - delete memif\n");
- printf ("\tshow - show connection details\n");
- printf ("\tip-set <index> <ip-addr> - set interface ip address\n");
- printf
- ("\trx-mode <index> <qid> <polling|interrupt> - set queue rx mode\n");
- printf ("\tsh-count - print counters\n");
- printf ("\tcl-count - clear counters\n");
- printf ("\tsend <index> <tx> <ip> <mac> - send icmp\n");
-}
-
-int
-icmpr_free ()
-{
- /* application cleanup */
- int err;
- long i;
- if (out_fd > 0)
- close (out_fd);
- out_fd = -1;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn)
- icmpr_memif_delete (i);
- }
-
- err = memif_cleanup ();
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
-
- return 0;
-}
-
-int
-icmpr_set_ip (long index, char *ip)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
- if (c->conn == NULL)
- {
- INFO ("no connection at index %ld", index);
- return 0;
- }
-
- char *end;
- char *ui;
- uint8_t tmp[4];
- ui = strtok (ip, ".");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 10);
-
- c->ip_addr[0] = tmp[0];
- c->ip_addr[1] = tmp[1];
- c->ip_addr[2] = tmp[2];
- c->ip_addr[3] = tmp[3];
-
- INFO ("memif %ld ip address set to %u.%u.%u.%u",
- index, c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
-
- return 0;
-
-error:
- INFO ("invalid ip address");
- return 0;
-}
-
-int
-icmpr_set_rx_mode (long index, long qid, char *mode)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- if (c->conn == NULL)
- {
- INFO ("no connection at index %ld", index);
- return 0;
- }
-
- if (strncmp (mode, "interrupt", 9) == 0)
- {
- memif_set_rx_mode (c->conn, MEMIF_RX_MODE_INTERRUPT, qid);
- }
-
- else if (strncmp (mode, "polling", 7) == 0)
- {
- memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, qid);
- }
- else
- INFO ("expected rx mode <interrupt|polling>");
- return 0;
-}
-
-void
-icmpr_print_counters ()
-{
- int i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn == NULL)
- continue;
- printf ("===============================\n");
- printf ("interface index: %d\n", c->index);
- printf ("\trx: %lu\n", c->rx_counter);
- printf ("\ttx: %lu\n", c->tx_counter);
- printf ("\ttx_err: %lu\n", c->tx_err_counter);
- printf ("\tts: %lus %luns\n", c->t_sec, c->t_nsec);
- }
-}
-
-void
-icmpr_reset_counters ()
-{
- int i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn == NULL)
- continue;
- c->t_sec = c->t_nsec = c->tx_err_counter = c->tx_counter =
- c->rx_counter = 0;
- }
-}
-
-void *
-icmpr_send_proc (void *data)
-{
- icmpr_thread_data_t *d = (icmpr_thread_data_t *) data;
- int index = d->index;
- uint64_t count = d->packet_num;
- memif_connection_t *c = &memif_connection[index];
- if (c->conn == NULL)
- {
- INFO ("No connection at index %d. Thread exiting...\n", index);
- pthread_exit (NULL);
- }
- uint16_t tx, i;
- int err = MEMIF_ERR_SUCCESS;
- uint32_t seq = 0;
- struct timespec start, end;
- memset (&start, 0, sizeof (start));
- memset (&end, 0, sizeof (end));
-
- timespec_get (&start, TIME_UTC);
- while (count)
- {
- i = 0;
- err = memif_buffer_alloc (c->conn, 0, c->tx_bufs,
- MAX_MEMIF_BUFS >
- count ? count : MAX_MEMIF_BUFS, &tx, 128);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s Thread exiting...\n",
- memif_strerror (err));
- pthread_exit (NULL);
- }
- c->tx_buf_num += tx;
-
- while (tx)
- {
- while (tx > 2)
- {
- generate_packet ((void *) c->tx_bufs[i].data,
- &c->tx_bufs[i].len, c->ip_addr,
- d->ip_daddr, d->hw_daddr, seq++);
- generate_packet ((void *) c->tx_bufs[i + 1].data,
- &c->tx_bufs[i + 1].len, c->ip_addr,
- d->ip_daddr, d->hw_daddr, seq++);
- i += 2;
- tx -= 2;
- }
- generate_packet ((void *) c->tx_bufs[i].data,
- &c->tx_bufs[i].len, c->ip_addr,
- d->ip_daddr, d->hw_daddr, seq++);
- i++;
- tx--;
- }
- err = memif_tx_burst (c->conn, 0, c->tx_bufs, c->tx_buf_num, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s Thread exiting...\n",
- memif_strerror (err));
- pthread_exit (NULL);
- }
- c->tx_buf_num -= tx;
- c->tx_counter += tx;
- count -= tx;
- }
- timespec_get (&end, TIME_UTC);
- printf ("\n\n");
- INFO ("Packet sequence finished!");
- INFO ("Seq len: %u", seq);
- uint64_t t1 = end.tv_sec - start.tv_sec;
- uint64_t t2;
- if (end.tv_nsec > start.tv_nsec)
- {
- t2 = end.tv_nsec - start.tv_nsec;
- }
- else
- {
- t2 = start.tv_nsec - end.tv_nsec;
- t1--;
- }
- c->t_sec = t1;
- c->t_nsec = t2;
- INFO ("Seq time: %lus %luns", t1, t2);
- double tmp = t1;
- tmp += t2 / 1e+9;
- tmp = seq / tmp;
- INFO ("Average pps: %f", tmp);
- INFO ("Thread exiting...");
- pthread_exit (NULL);
-}
-
-int
-icmpr_send (long index, long packet_num, char *hw, char *ip)
-{
- memif_connection_t *c = &memif_connection[index];
- char *end;
- char *ui;
- uint8_t tmp[6];
- if (c->conn == NULL)
- return -1;
- c->seq = 0;
- icmpr_thread_data_t *data = &icmpr_thread_data[index];
- data->index = index;
- data->packet_num = packet_num;
- INFO ("PN: %lu", data->packet_num);
- printf ("%s\n", ip);
- printf ("%s\n", hw);
-
- ui = strtok (ip, ".");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 10);
-
- data->ip_daddr[0] = tmp[0];
- data->ip_daddr[1] = tmp[1];
- data->ip_daddr[2] = tmp[2];
- data->ip_daddr[3] = tmp[3];
-
- ui = strtok (hw, ":");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[4] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[5] = strtol (ui, &end, 16);
-
- data->hw_daddr[0] = tmp[0];
- data->hw_daddr[1] = tmp[1];
- data->hw_daddr[2] = tmp[2];
- data->hw_daddr[3] = tmp[3];
- data->hw_daddr[4] = tmp[4];
- data->hw_daddr[5] = tmp[5];
-
- pthread_create (&thread[index], NULL, icmpr_send_proc, (void *) data);
- return 0;
-
-error:
- INFO ("Invalid input\n");
- return 0;
-}
-
-int
-user_input_handler ()
-{
- char *in = (char *) malloc (256);
- char *ui = fgets (in, 256, stdin);
- char *end;
- long a;
- if (in[0] == '\n')
- goto done;
- ui = strtok (in, " ");
- if (strncmp (ui, "exit", 4) == 0)
- {
- free (in);
- icmpr_free ();
- exit (EXIT_SUCCESS);
- }
- else if (strncmp (ui, "help", 4) == 0)
- {
- print_help ();
- goto done;
- }
- else if (strncmp (ui, "conn", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_create (a, strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected mode <0|1>");
- goto done;
- }
- else if (strncmp (ui, "del", 3) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_delete (strtol (ui, &end, 10));
- else
- INFO ("expected id");
- goto done;
- }
- else if (strncmp (ui, "show", 4) == 0)
- {
- print_memif_details ();
- goto done;
- }
- else if (strncmp (ui, "ip-set", 6) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_set_ip (strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected id");
- goto done;
- }
- else if (strncmp (ui, "rx-mode", 7) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_set_rx_mode (a, strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected qid");
- goto done;
- }
- else if (strncmp (ui, "sh-count", 8) == 0)
- {
- icmpr_print_counters ();
- }
- else if (strncmp (ui, "cl-count", 8) == 0)
- {
- icmpr_reset_counters ();
- }
- else if (strncmp (ui, "send", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_send (a, strtol (ui, &end, 10), strtok (NULL, " "),
- strtok (NULL, " "));
- else
- INFO ("expected count");
- goto done;
- }
- else
- {
- INFO ("unknown command: %s", ui);
- goto done;
- }
-
- return 0;
-done:
- free (in);
- return 0;
-}
-
-int
-poll_event (int timeout)
-{
- struct epoll_event evt;
- int app_err = 0, memif_err = 0, en = 0;
- uint32_t events = 0;
- struct timespec start, end;
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
- sigset_t sigset;
- sigemptyset (&sigset);
- en = epoll_pwait (epfd, &evt, 1, timeout, &sigset);
- /* id event polled */
- timespec_get (&start, TIME_UTC);
- if (en < 0)
- {
- DBG ("epoll_pwait: %s", strerror (errno));
- return -1;
- }
- if (en > 0)
- {
- /* this app does not use any other file descriptors than stds and memif control fds */
- if (evt.data.fd > 2)
- {
- /* event of memif control fd */
- /* convert epoll events to memif events */
- if (evt.events & EPOLLIN)
- events |= MEMIF_FD_EVENT_READ;
- if (evt.events & EPOLLOUT)
- events |= MEMIF_FD_EVENT_WRITE;
- if (evt.events & EPOLLERR)
- events |= MEMIF_FD_EVENT_ERROR;
- memif_err = memif_control_fd_handler (evt.data.fd, events);
- if (memif_err != MEMIF_ERR_SUCCESS)
- INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err));
- }
- else if (evt.data.fd == 0)
- {
- app_err = user_input_handler ();
- }
- else
- {
- DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd);
- }
- }
-
- timespec_get (&end, TIME_UTC);
- LOG ("interrupt: %ld", end.tv_nsec - start.tv_nsec);
-
- if ((app_err < 0) || (memif_err < 0))
- {
- if (app_err < 0)
- DBG ("user input handler error");
- if (memif_err < 0)
- DBG ("memif control fd handler error");
- return -1;
- }
-
- return 0;
-}
-
-int
-main ()
-{
- epfd = epoll_create (1);
- add_epoll_fd (0, EPOLLIN);
-
-#ifdef LOG_FILE
- remove (LOG_FILE);
- enable_log = 0;
-
- out_fd = open (LOG_FILE, O_WRONLY | O_CREAT, S_IRWXO);
- if (out_fd < 0)
- INFO ("Error opening log file: %s", strerror (errno));
-#endif /* LOG_FILE */
-
- /* initialize memory interface */
- int err, i;
- /* if valid callback is passed as argument, fd event polling will be done by user
- all file descriptors and events will be passed to user in this callback */
- /* if callback is set to NULL libmemif will handle fd event polling */
- err = memif_init (control_fd_update, APP_NAME, NULL, NULL, NULL);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_init: %s", memif_strerror (err));
- icmpr_free ();
- exit (-1);
- }
-
- for (i = 0; i < MAX_CONNS; i++)
- {
- memset (&memif_connection[i], 0, sizeof (memif_connection_t));
- ctx[i] = i;
- }
-
- print_help ();
-
- /* main loop */
- while (1)
- {
- if (poll_event (-1) < 0)
- {
- DBG ("poll_event error!");
- }
- }
-}
diff --git a/extras/libmemif/examples/icmp_responder-mt/main.c b/extras/libmemif/examples/icmp_responder-mt/main.c
deleted file mode 100644
index f37c6a026fb..00000000000
--- a/extras/libmemif/examples/icmp_responder-mt/main.c
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 <stdlib.h>
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-#include <netinet/if_ether.h>
-#include <net/if_arp.h>
-#include <asm/byteorder.h>
-#include <byteswap.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <errno.h>
-#include <unistd.h>
-#include <signal.h>
-#include <pthread.h>
-
-#include <libmemif.h>
-#include <icmp_proto.h>
-
-#define APP_NAME "ICMP_Responder"
-#define IF_NAME "memif_connection"
-
-
-#ifdef ICMP_DBG
-#define DBG(...) do { \
- printf (APP_NAME":%s:%d: ", __func__, __LINE__); \
- printf (__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-#else
-#define DBG(...)
-#endif
-
-#define INFO(...) do { \
- printf ("INFO: "__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-
-/* maximum tx/rx memif buffers */
-#define MAX_MEMIF_BUFS 256
-#define MAX_CONNS 50
-#define MAX_QUEUES 2
-#define MAX_THREADS ((MAX_CONNS) * (MAX_QUEUES))
-
-int main_epfd;
-
-typedef struct
-{
- /* thread id */
- uint8_t id;
- /* memif connection index */
- uint16_t index;
- /* id of queue to be handled by thread */
- uint8_t qid;
- uint8_t isRunning;
-
- uint16_t rx_buf_num;
- uint16_t tx_buf_num;
- memif_buffer_t *rx_bufs;
- memif_buffer_t *tx_bufs;
-} memif_thread_data_t;
-
-typedef struct
-{
- uint16_t index;
- /* memif connection handle */
- memif_conn_handle_t conn;
- /* interface ip address */
- uint8_t ip_addr[4];
- /* inform pthread about connection termination */
- uint8_t pending_del;
-} memif_connection_t;
-
-memif_connection_t memif_connection[MAX_CONNS];
-long ctx[MAX_CONNS];
-
-/* thread data specific for each thread */
-memif_thread_data_t thread_data[MAX_THREADS];
-pthread_t thread[MAX_THREADS];
-
-void
-user_signal_handler (int sig)
-{
-}
-
-static void
-print_memif_details ()
-{
- memif_details_t md;
- ssize_t buflen;
- char *buf;
- int err, i, e, ti;
- buflen = 2048;
- buf = malloc (buflen);
- printf ("MEMIF DETAILS\n");
- printf ("==============================\n");
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
-
- memset (&md, 0, sizeof (md));
- memset (buf, 0, buflen);
-
- err = memif_get_details (c->conn, &md, buf, buflen);
- if (err != MEMIF_ERR_SUCCESS)
- {
- if (err != MEMIF_ERR_NOCONN)
- INFO ("%s", memif_strerror (err));
- continue;
- }
-
- printf ("interface index: %d\n", i);
-
- printf ("\tinterface ip: %u.%u.%u.%u\n",
- c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
- printf ("\tinterface name: %s\n", (char *) md.if_name);
- printf ("\tapp name: %s\n", (char *) md.inst_name);
- printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
- printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
- printf ("\tid: %u\n", md.id);
- printf ("\tsecret: %s\n", (char *) md.secret);
- printf ("\trole: ");
- if (md.role)
- printf ("slave\n");
- else
- printf ("master\n");
- printf ("\tmode: ");
- switch (md.mode)
- {
- case 0:
- printf ("ethernet\n");
- break;
- case 1:
- printf ("ip\n");
- break;
- case 2:
- printf ("punt/inject\n");
- break;
- default:
- printf ("unknown\n");
- break;
- }
- printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
- printf ("\trx queues:\n");
- for (e = 0; e < md.rx_queues_num; e++)
- {
- ti = (i * MAX_QUEUES) + e;
- printf ("\tqueue id: %u\n", md.rx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
- printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
- printf ("\t\tthread id: %u\n", thread_data[ti].id);
- printf ("\t\tthread connection index: %u\n", thread_data[ti].index);
- printf ("\t\tthread running: ");
- if (thread_data[ti].isRunning)
- printf ("yes\n");
- else
- printf ("no");
- }
- printf ("\ttx queues:\n");
- for (e = 0; e < md.tx_queues_num; e++)
- {
- printf ("\tqueue id: %u\n", md.tx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
- printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
- }
- printf ("\tlink: ");
- if (md.link_up_down)
- printf ("up\n");
- else
- printf ("down\n");
- }
- free (buf);
-}
-
-int
-add_epoll_fd (int epfd, int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d added to epoll", fd);
- return 0;
-}
-
-int
-mod_epoll_fd (int epfd, int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d modified on epoll", fd);
- return 0;
-}
-
-int
-del_epoll_fd (int epfd, int fd)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d removed from epoll", fd);
- return 0;
-}
-
-void *
-memif_rx_poll (void *ptr)
-{
- memif_thread_data_t *data = (memif_thread_data_t *) ptr;
- memif_connection_t *c = &memif_connection[data->index];
- int err;
- uint16_t rx = 0, tx = 0, fb = 0;
-
- data->rx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- data->tx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- data->rx_buf_num = 0;
- data->tx_buf_num = 0;
-
- data->isRunning = 1;
- INFO ("pthread id %u starts in polling mode", data->id);
-
- while (1)
- {
- if (c->pending_del)
- goto close;
-
- /* receive data from shared memory buffers */
- err =
- memif_rx_burst (c->conn, data->qid, data->rx_bufs, MAX_MEMIF_BUFS,
- &rx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- data->rx_buf_num += rx;
- goto error;
- }
- data->rx_buf_num += rx;
- if (rx == 0)
- {
- continue;
- }
-
- DBG ("thread id: %u", data->id);
-
- DBG ("received %d buffers. %u/%u alloc/free buffers",
- rx, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num);
-
- err =
- memif_buffer_alloc (c->conn, data->qid, data->tx_bufs,
- data->rx_buf_num, &tx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- data->tx_buf_num += tx;
- goto error;
- }
- data->tx_buf_num += tx;
- DBG ("allocated %d/%d buffers, %u free buffers",
- tx, data->rx_buf_num, MAX_MEMIF_BUFS - data->tx_buf_num);
-
- int i;
- for (i = 0; i < rx; i++)
- {
- resolve_packet ((void *) (data->rx_bufs + i)->data,
- (data->rx_bufs + i)->len,
- (void *) (data->tx_bufs + i)->data,
- &(data->tx_bufs + i)->len, c->ip_addr);
- }
-
- /* mark memif buffers and shared memory buffers as free */
- err = memif_refill_queue (c->conn, data->qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- data->rx_buf_num -= fb;
-
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num);
-
- err =
- memif_tx_burst (c->conn, data->qid, data->tx_bufs, data->tx_buf_num,
- &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- DBG ("tx: %d/%u", tx, data->tx_buf_num);
- data->tx_buf_num -= tx;
- }
-
-error:
- INFO ("thread %u error!", data->id);
- goto close;
-
-close:
- err = memif_refill_queue (c->conn, data->qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- data->rx_buf_num -= fb;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num);
- free (data->rx_bufs);
- free (data->tx_bufs);
- data->isRunning = 0;
- INFO ("pthread id %u exit", data->id);
- pthread_exit (NULL);
-}
-
-void *
-memif_rx_interrupt (void *ptr)
-{
- memif_thread_data_t *data = (memif_thread_data_t *) ptr;
- memif_connection_t *c = &memif_connection[data->index];
- int err;
- uint16_t rx = 0, tx = 0, fb = 0;
- struct epoll_event evt;
- int en = 0;
- sigset_t sigset;
-
- signal (SIGUSR1, user_signal_handler);
-
- data->rx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- data->tx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- data->rx_buf_num = 0;
- data->tx_buf_num = 0;
-
- data->isRunning = 1;
- INFO ("pthread id %u starts in interrupt mode", data->id);
- int thread_epfd = epoll_create (1);
-
- /* get interrupt queue id */
- int fd = -1;
- err = memif_get_queue_efd (c->conn, data->qid, &fd);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_get_queue_efd: %s", memif_strerror (err));
- goto error;
- }
- add_epoll_fd (thread_epfd, fd, EPOLLIN);
-
- while (1)
- {
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
- sigemptyset (&sigset);
- en = epoll_pwait (thread_epfd, &evt, 1, -1, &sigset);
- if (en < 0)
- {
- if (errno == EINTR)
- goto close;
- DBG ("epoll_pwait: %s", strerror (errno));
- goto error;
- }
- else if (en > 0)
- {
- /* receive data from shared memory buffers */
- err =
- memif_rx_burst (c->conn, data->qid, data->rx_bufs, MAX_MEMIF_BUFS,
- &rx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- data->rx_buf_num += rx;
- goto error;
- }
- data->rx_buf_num += rx;
- if (rx == 0)
- {
- continue;
- }
-
- DBG ("thread id: %u", data->id);
-
- DBG ("received %d buffers. %u/%u alloc/free buffers",
- rx, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num);
-
- err =
- memif_buffer_alloc (c->conn, data->qid, data->tx_bufs,
- data->rx_buf_num, &tx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- data->tx_buf_num += tx;
- goto error;
- }
- data->tx_buf_num += tx;
- DBG ("allocated %d/%d buffers, %u free buffers",
- tx, data->rx_buf_num, MAX_MEMIF_BUFS - data->tx_buf_num);
-
- int i;
- for (i = 0; i < rx; i++)
- {
- resolve_packet ((void *) (data->rx_bufs + i)->data,
- (data->rx_bufs + i)->len,
- (void *) (data->tx_bufs + i)->data,
- &(data->tx_bufs + i)->len, c->ip_addr);
- }
-
- /* mark memif buffers and shared memory buffers as free */
- err = memif_refill_queue (c->conn, data->qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- data->rx_buf_num -= fb;
-
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num);
-
- err =
- memif_tx_burst (c->conn, data->qid, data->tx_bufs,
- data->tx_buf_num, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- DBG ("tx: %d/%u", tx, data->tx_buf_num);
- data->tx_buf_num -= tx;
- }
- }
-
-error:
- INFO ("thread %u error!", data->id);
- goto close;
-
-close:
- err = memif_refill_queue (c->conn, data->qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- data->rx_buf_num -= fb;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num);
- free (data->rx_bufs);
- free (data->tx_bufs);
- data->isRunning = 0;
- INFO ("pthread id %u exit", data->id);
- pthread_exit (NULL);
-
-}
-
-/* informs user about connected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
- long index = (*(long *) private_ctx);
- int err, i, ti;
- INFO ("memif connected! index %ld", index);
- memif_connection_t *c = &memif_connection[index];
- c->pending_del = 0;
-
- for (i = 0; i < MAX_QUEUES; i++)
- {
- err = memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, i);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_set_rx_mode: %s qid: %u", memif_strerror (err), i);
- else
- {
- ti = (index * MAX_QUEUES) + i;
- if (thread_data[ti].isRunning)
- {
- INFO ("thread id: %d already running!", ti);
- continue;
- }
- thread_data[ti].index = index;
- thread_data[ti].qid = i;
- thread_data[ti].id = ti;
- if ((i % 2) == 0)
- pthread_create (&thread[ti],
- NULL, memif_rx_poll, (void *) &thread_data[ti]);
- else
- pthread_create (&thread[ti],
- NULL, memif_rx_interrupt,
- (void *) &thread_data[ti]);
- }
-
- }
- return 0;
-}
-
-/* informs user about disconnected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_disconnect (memif_conn_handle_t conn, void *private_ctx)
-{
- void *ptr;
- long index = (*(long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- int i, ti;
- INFO ("memif disconnected!");
- /* inform thread in polling mode about memif disconnection */
- c->pending_del = 1;
- for (i = 0; i < MAX_QUEUES; i++)
- {
- ti = (index * MAX_QUEUES) + i;
- if (!thread_data[ti].isRunning)
- continue;
- if ((i % 2) != 0)
- pthread_kill (thread[ti], SIGUSR1); /* interrupt thread in interrupt mode */
- pthread_join (thread[ti], &ptr);
- }
- return 0;
-}
-
-/* user needs to watch new fd or stop watching fd that is about to be closed.
- control fd will be modified during connection establishment to minimize CPU usage */
-int
-control_fd_update (int fd, uint8_t events, void *ctx)
-{
- /* convert memif event definitions to epoll events */
- if (events & MEMIF_FD_EVENT_DEL)
- return del_epoll_fd (main_epfd, fd);
-
- uint32_t evt = 0;
- if (events & MEMIF_FD_EVENT_READ)
- evt |= EPOLLIN;
- if (events & MEMIF_FD_EVENT_WRITE)
- evt |= EPOLLOUT;
-
- if (events & MEMIF_FD_EVENT_MOD)
- return mod_epoll_fd (main_epfd, fd, evt);
-
- return add_epoll_fd (main_epfd, fd, evt);
-}
-
-int
-icmpr_memif_create (long index)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- /* setting memif connection arguments */
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.is_master = 0;
- args.log2_ring_size = 10;
- args.buffer_size = 2048;
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
- strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
- args.mode = 0;
- /* socket filename is not specified, because this app is supposed to
- connect to VPP over memif. so default socket filename will be used */
- /* default socketfile = /run/vpp/memif.sock */
-
- args.interface_id = index;
- /* last argument for memif_create (void * private_ctx) is used by user
- to identify connection. this context is returned with callbacks */
- int err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, NULL,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
-
- c->index = index;
-
- c->ip_addr[0] = 192;
- c->ip_addr[1] = 168;
- c->ip_addr[2] = c->index + 1;
- c->ip_addr[3] = 2;
- return 0;
-}
-
-int
-icmpr_memif_delete (long index)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- int err;
- /* disconnect then delete memif connection */
- err = memif_delete (&c->conn);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
- return 0;
-}
-
-void
-print_help ()
-{
- printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
-#ifdef ICMP_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("==============================\n");
- printf ("libmemif version: %s", LIBMEMIF_VERSION);
-#ifdef MEMIF_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("memif version: %d\n", memif_get_version ());
- printf ("commands:\n");
- printf ("\thelp - prints this help\n");
- printf ("\texit - exit app\n");
- printf ("\tconn <index> - create memif (slave-mode)\n");
- printf ("\tdel <index> - delete memif\n");
- printf ("\tshow - show connection details\n");
- printf ("\tip-set <index> <ip-addr> - set interface ip address\n");
-}
-
-int
-icmpr_free ()
-{
- /* application cleanup */
- int err;
- long i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn)
- icmpr_memif_delete (i);
- }
-
- err = memif_cleanup ();
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
-
- return 0;
-}
-
-int
-icmpr_set_ip (long index, char *ip)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
- if (c->conn == NULL)
- {
- INFO ("no connection at index %ld", index);
- return 0;
- }
-
- char *end;
- char *ui;
- uint8_t tmp[4];
- ui = strtok (ip, ".");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 10);
-
- c->ip_addr[0] = tmp[0];
- c->ip_addr[1] = tmp[1];
- c->ip_addr[2] = tmp[2];
- c->ip_addr[3] = tmp[3];
-
- INFO ("memif %ld ip address set to %u.%u.%u.%u",
- index, c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
-
- return 0;
-
-error:
- INFO ("invalid ip address");
- return 0;
-}
-
-
-int
-user_input_handler ()
-{
- char *in = (char *) malloc (256);
- char *ui = fgets (in, 256, stdin);
- char *end;
- if (in[0] == '\n')
- goto done;
- ui = strtok (in, " ");
- if (strncmp (ui, "exit", 4) == 0)
- {
- free (in);
- icmpr_free ();
- exit (EXIT_SUCCESS);
- }
- else if (strncmp (ui, "help", 4) == 0)
- {
- print_help ();
- goto done;
- }
- else if (strncmp (ui, "conn", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_create (strtol (ui, &end, 10));
- else
- INFO ("expected id");
- goto done;
- }
- else if (strncmp (ui, "del", 3) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_delete (strtol (ui, &end, 10));
- else
- INFO ("expected id");
- goto done;
- }
- else if (strncmp (ui, "show", 4) == 0)
- {
- print_memif_details ();
- goto done;
- }
- else if (strncmp (ui, "ip-set", 6) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_set_ip (strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected id");
- goto done;
- }
- else
- {
- DBG ("unknown command: %s", ui);
- goto done;
- }
-
- return 0;
-done:
- free (in);
- return 0;
-}
-
-int
-poll_event (int timeout)
-{
- struct epoll_event evt;
- int app_err = 0, memif_err = 0, en = 0;
- uint32_t events = 0;
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
- sigset_t sigset;
- sigemptyset (&sigset);
- en = epoll_pwait (main_epfd, &evt, 1, timeout, &sigset);
- if (en < 0)
- {
- DBG ("epoll_pwait: %s", strerror (errno));
- return -1;
- }
- if (en > 0)
- {
- /* this app does not use any other file descriptors than stds and memif control fds */
- if (evt.data.fd > 2)
- {
- /* event of memif control fd */
- /* convert epoll events to memif events */
- if (evt.events & EPOLLIN)
- events |= MEMIF_FD_EVENT_READ;
- if (evt.events & EPOLLOUT)
- events |= MEMIF_FD_EVENT_WRITE;
- if (evt.events & EPOLLERR)
- events |= MEMIF_FD_EVENT_ERROR;
- memif_err = memif_control_fd_handler (evt.data.fd, events);
- if (memif_err != MEMIF_ERR_SUCCESS)
- INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err));
- }
- else if (evt.data.fd == 0)
- {
- app_err = user_input_handler ();
- }
- else
- {
- DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd);
- }
- }
-
- if ((app_err < 0) || (memif_err < 0))
- {
- if (app_err < 0)
- DBG ("user input handler error");
- if (memif_err < 0)
- DBG ("memif control fd handler error");
- return -1;
- }
-
- return 0;
-}
-
-int
-main ()
-{
- main_epfd = epoll_create (1);
- add_epoll_fd (main_epfd, 0, EPOLLIN);
-
- /* initialize memory interface */
- int err, i;
- /* if valid callback is passed as argument, fd event polling will be done by user
- all file descriptors and events will be passed to user in this callback */
- /* if callback is set to NULL libmemif will handle fd event polling */
- err = memif_init (control_fd_update, APP_NAME, NULL, NULL, NULL);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_init: %s", memif_strerror (err));
-
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection[i].conn = NULL;
- ctx[i] = i;
- }
-
- memset (&thread_data, 0, sizeof (memif_thread_data_t) * MAX_THREADS);
-
- print_help ();
-
- /* main loop */
- while (1)
- {
- if (poll_event (-1) < 0)
- {
- DBG ("poll_event error!");
- }
- }
-}
diff --git a/extras/libmemif/examples/icmp_responder-mt_3-1/main.c b/extras/libmemif/examples/icmp_responder-mt_3-1/main.c
deleted file mode 100644
index 86c57d192e3..00000000000
--- a/extras/libmemif/examples/icmp_responder-mt_3-1/main.c
+++ /dev/null
@@ -1,549 +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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <unistd.h>
-
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-
-#include <libmemif.h>
-#include <icmp_proto.h>
-
-
-#define APP_NAME "ICMP_Responder_mt_v3.1"
-#define IF_NAME "memif_connection"
-
-#ifdef ICMP_DBG
-#define DBG(...) do { \
- printf (APP_NAME":%s:%d: ", __func__, __LINE__); \
- printf (__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-#else
-#define DBG(...)
-#endif
-
-#define ICMPR_BUFFER_LENGTH 32
-#define ICMPR_SOCKET_FILENAME_LEN 256
-#define ICMPR_MEMIF_BUFFER_NUM 256
-
-static struct option options[] = {
- {"threads", required_argument, 0, 't'},
- {"if_num", required_argument, 0, 'i'}
-};
-
-struct memif_connection
-{
- uint16_t id; /* unique interface id */
- bool connected; /* is connected */
- struct per_thread_data *ptd; /* per thread data */
- memif_conn_handle_t handle; /* memif connection handle */
- uint8_t ip_addr[4]; /* ip4 address */
-};
-
-struct per_thread_data
-{
- bool running; /* is thread main loop running */
- uint8_t index; /* thread index */
- int epfd; /* epoll file descriptor */
- int pcfd; /* poll cancel file descriptor */
- uint16_t if_num; /* number of interfaces on this thread */
- struct memif_connection *conns; /* memif connections pool */
- memif_per_thread_main_handle_t pt_main; /* memif per thread main handle */
- memif_socket_handle_t socket_handle; /* memif socket handle */
-};
-
-struct icmpr_main
-{
- uint8_t threads; /* number of threads */
- uint16_t per_thread_if_num; /* number of interfaces per thread */
- struct per_thread_data *ptd; /* per thread data pool */
- pthread_t *pthread; /* thread pool */
-};
-
-struct icmpr_main icmpr_main;
-
-int
-add_epoll_fd (int epfd, int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d added to epoll", fd);
- return 0;
-}
-
-int
-mod_epoll_fd (int epfd, int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d modified on epoll", fd);
- return 0;
-}
-
-int
-del_epoll_fd (int epfd, int fd)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d removed from epoll", fd);
- return 0;
-}
-
-/* Called when libmemif requests an update on any of its file descriptors */
-static int
-control_fd_update (int fd, uint8_t events, void *private_ctx)
-{
- struct per_thread_data *ptd = (struct per_thread_data *) private_ctx;
- uint32_t evt = 0;
-
- if (ptd == NULL)
- return -1;
-
- /* convert memif event definitions to epoll events */
- if (events & MEMIF_FD_EVENT_DEL)
- return del_epoll_fd (ptd->epfd, fd);
-
- if (events & MEMIF_FD_EVENT_READ)
- evt |= EPOLLIN;
- if (events & MEMIF_FD_EVENT_WRITE)
- evt |= EPOLLOUT;
-
- if (events & MEMIF_FD_EVENT_MOD)
- return mod_epoll_fd (ptd->epfd, fd, evt);
-
- return add_epoll_fd (ptd->epfd, fd, evt);
-}
-
-static int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
- struct per_thread_data *ptd = (struct per_thread_data *) private_ctx;
- struct memif_connection *c;
- int i = 0;
-
- while (i < ptd->if_num && ptd->conns[i].handle != conn)
- i++;
- c = &ptd->conns[i];
-
- c->connected = true;
- DBG ("Connected: %u", c->id);
-
- memif_refill_queue (conn, 0, -1, 0);
-
- return 0;
-}
-
-static int
-on_disconnect (memif_conn_handle_t conn, void *private_ctx)
-{
- struct per_thread_data *ptd = (struct per_thread_data *) private_ctx;
- struct memif_connection *c;
- int i = 0;
-
- while (i < ptd->if_num && ptd->conns[i].handle != conn)
- i++;
- c = &ptd->conns[i];
-
- c->connected = false;
- DBG ("Disconnected: %u", c->id);
-
- return 0;
-}
-
-static int
-on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- struct per_thread_data *ptd = (struct per_thread_data *) private_ctx;
- struct memif_connection *c;
- memif_buffer_t mbufs[ICMPR_MEMIF_BUFFER_NUM];
- uint16_t rx = 0;
- uint16_t tx = 0;
- uint16_t ret;
- memif_err_t err;
- int i = 0;
-
- memset (mbufs, 0, sizeof (memif_buffer_t) * ICMPR_MEMIF_BUFFER_NUM);
-
- while (i < ptd->if_num && ptd->conns[i].handle != conn)
- i++;
- c = &ptd->conns[i];
-
- /* receive data from shared memory buffers */
- err = memif_rx_burst (conn, qid, mbufs, ICMPR_MEMIF_BUFFER_NUM, &rx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- printf ("memif_rx_burst: %s\n", memif_strerror (err));
- goto error;
- }
-
- /* resolve packet in place (zer-copy slave) */
- for (i = 0; i < rx; i++)
- resolve_packet2 (mbufs[i].data, &mbufs[i].len, c->ip_addr);
-
- /* enqueue received buffers */
- err = memif_buffer_enq_tx (conn, qid, mbufs, i, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- printf ("memif_rx_burst: %s\n", memif_strerror (err));
- goto error;
- }
-
- /* mark shared memory buffers as free */
- err = memif_refill_queue (conn, qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- {
- printf ("memif_rx_burst: %s\n", memif_strerror (err));
- goto error;
- }
-
- err = memif_tx_burst (conn, qid, mbufs, tx, &ret);
- if (err != MEMIF_ERR_SUCCESS)
- {
- printf ("memif_rx_burst: %s\n", memif_strerror (err));
- goto error;
- }
-
- return 0;
-
-error:
- memif_refill_queue (conn, qid, -1, 0);
- return -1;
-}
-
-int
-poll_event (memif_per_thread_main_handle_t pt_main, int pcfd, int epfd,
- int timeout)
-{
- struct epoll_event evt;
- int en = 0;
- uint8_t events = 0;
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
-
- en = epoll_pwait (epfd, &evt, 1, timeout, NULL);
- if (en < 0)
- {
- printf ("epoll_pwait: %s\n", strerror (errno));
- return -1;
- }
-
- if (en > 0)
- {
- /* Cancel event polling */
- if (evt.data.fd == pcfd)
- return 1;
-
- if (evt.events & EPOLLIN)
- events |= MEMIF_FD_EVENT_READ;
- if (evt.events & EPOLLOUT)
- events |= MEMIF_FD_EVENT_WRITE;
- if (evt.events & EPOLLERR)
- events |= MEMIF_FD_EVENT_ERROR;
-
- /* No need to use locks, as the database is separated */
- memif_per_thread_control_fd_handler (pt_main, evt.data.fd, events);
- }
-
- return 0;
-}
-
-static void *
-icmpr_thread_fn (void *data)
-{
- struct per_thread_data *ptd = (struct per_thread_data *) data;
- int rv;
- uint16_t i;
- char socket_filename[ICMPR_SOCKET_FILENAME_LEN] = "/run/vpp/memif";
- memif_conn_args_t args;
-
- ptd->epfd = epoll_create (1);
-
- ptd->conns = malloc (sizeof (struct memif_connection) * ptd->if_num);
- if (ptd->conns == NULL)
- {
- printf ("%s\n", strerror (errno));
- return NULL;
- }
-
- memset (ptd->conns, 0, sizeof (struct memif_connection) * ptd->if_num);
-
- /* Initialize memif database (per thread). */
- rv =
- memif_per_thread_init (&ptd->pt_main, ptd, control_fd_update, APP_NAME,
- NULL, NULL, NULL);
- if (rv != MEMIF_ERR_SUCCESS)
- {
- printf ("memif_per_thread_init: %s\n", memif_strerror (rv));
- return NULL;
- }
-
- /* Create unique socket. Each thread requires a unique socket. Interfaces created
- * on the same thread can share one socket.
- */
- socket_filename[strlen (socket_filename)] = '0' + ptd->index;
- strncpy (socket_filename + strlen (socket_filename), ".sock", 5);
- DBG ("socket_filename: %s", socket_filename);
-
- rv = memif_per_thread_create_socket (ptd->pt_main, &ptd->socket_handle,
- socket_filename, ptd);
- if (rv != MEMIF_ERR_SUCCESS)
- {
- printf ("memif_per_thread_create_socket: %s\n", memif_strerror (rv));
- return NULL;
- }
-
- /* Create interfaces on this thread */
- for (i = 0; i < ptd->if_num; i++)
- {
- ptd->conns[i].ip_addr[0] = 192;
- ptd->conns[i].ip_addr[1] = 168;
- ptd->conns[i].ip_addr[2] = ptd->index + 1;
- ptd->conns[i].ip_addr[3] = i * 2 + 2;
-
- memset (&args, 0, sizeof (args));
-
- args.socket = ptd->socket_handle;
- ptd->conns[i].id = i;
- args.interface_id = i;
-
- rv = memif_create (&ptd->conns[i].handle, &args, on_connect,
- on_disconnect, on_interrupt, ptd);
- if (rv < 0)
- {
- printf ("%s\n", memif_strerror (rv));
- return NULL;
- }
- }
-
- /* Poll cancel file descriptor. When an event is received on this fd, exit thread
- * loop in respective thread.
- */
- ptd->pcfd = eventfd (0, EFD_NONBLOCK);
- if (ptd->pcfd < 0)
- {
- printf ("eventfd: %s\n", strerror (errno));
- return NULL;
- }
- if (add_epoll_fd (ptd->epfd, ptd->pcfd, EPOLLIN) < 0)
- {
- printf ("Failed to add poll cancel fd to epfd.");
- return NULL;
- }
-
- /* Thread loop */
- ptd->running = true;
- while (ptd->running)
- {
- rv = poll_event (ptd->pt_main, ptd->pcfd, ptd->epfd, -1);
- if (rv != 0)
- ptd->running = false;
- }
-
- /* Clean up */
- for (i = 0; i < ptd->if_num; i++)
- memif_delete (&ptd->conns[i].handle);
-
- memif_delete_socket (&ptd->socket_handle);
-
- memif_per_thread_cleanup (&ptd->pt_main);
-
- free (ptd->conns);
- close (ptd->pcfd);
-
- return NULL;
-}
-
-static void
-icmpr_print_help ()
-{
- printf
- ("exit - Exits the application.\nhelp - Print this help.\nshow - Show memif interfaces\n");
-}
-
-static void
-icmpr_show_memifs ()
-{
- struct icmpr_main *im = &icmpr_main;
- int i, j;
- memif_socket_handle_t sh;
-
- printf ("%u Threads %u Memifs (per thread)\n", im->threads,
- im->per_thread_if_num);
- printf ("=================================\n");
-
- for (i = 0; i < im->threads; i++)
- {
- sh = im->ptd[i].socket_handle;
- printf ("Thread %u %s\n", i, memif_get_socket_filename (sh));
- for (j = 0; j < im->per_thread_if_num; j++)
- {
- printf ("\tMemif id %u\n\t%s\n", im->ptd[i].conns[j].id,
- im->ptd[i].conns[j].connected ? "Link up" : "Link down");
- }
- }
-}
-
-int
-main (int argc, char **argv)
-{
- struct icmpr_main *im = &icmpr_main;
- int rv, i;
- int option_index = 0;
- bool running;
- char buffer[ICMPR_BUFFER_LENGTH];
- uint64_t b = 1;
-
- memset (im, 0, sizeof (struct icmpr_main));
-
- /* Default args */
- im->threads = 4;
- im->per_thread_if_num = 1;
-
- /* Parse args */
- while ((rv =
- getopt_long (argc, argv, "t:i:", options, &option_index)) != (-1))
- {
- switch (rv)
- {
- case 't':
- im->threads = strtoul (optarg, NULL, 10);
- break;
- case 'i':
- im->per_thread_if_num = strtoul (optarg, NULL, 10);
- break;
- default:
- break;
- }
- }
-
- /* Check args */
- if (im->threads < 1)
- {
- printf ("threads < 1\n");
- exit (EXIT_FAILURE);
- }
-
- if (im->per_thread_if_num < 1)
- {
- printf ("if_num < 1\n");
- exit (EXIT_FAILURE);
- }
-
- /* Allocate memory */
- im->ptd = malloc (sizeof (struct per_thread_data) * im->threads);
- if (im->ptd == NULL)
- {
- printf ("%s\n", strerror (errno));
- return -1;
- }
- im->pthread = malloc (sizeof (pthread_t) * im->threads);
- if (im->pthread == NULL)
- {
- printf ("%s\n", strerror (errno));
- return -1;
- }
-
- /* Initialize and create threads */
- for (i = 0; i < im->threads; i++)
- {
- im->ptd[i].index = i;
- im->ptd[i].if_num = im->per_thread_if_num;
- pthread_create (&im->pthread[i], NULL, icmpr_thread_fn, &im->ptd[i]);
- }
-
- icmpr_print_help ();
-
- /* Main loop */
- running = true;
- while (running)
- {
- printf ("cmd: ");
- memset (buffer, 0, ICMPR_BUFFER_LENGTH);
- if (fgets (buffer, ICMPR_BUFFER_LENGTH, stdin) != buffer)
- {
- printf ("%s\n", strerror (errno));
- running = false;
- }
-
- if (strncmp (buffer, "exit", 4) == 0)
- running = false;
- else if (strncmp (buffer, "help", 4) == 0)
- icmpr_print_help ();
- else if (strncmp (buffer, "show", 4) == 0)
- icmpr_show_memifs ();
- }
-
- for (i = 0; i < im->threads; i++)
- {
- /* Stop polling */
- rv = write (im->ptd[i].pcfd, &b, sizeof (b));
- if (rv < 0)
- {
- printf ("Failed to cancel polling. %s\n", strerror (errno));
- exit (EXIT_FAILURE);
- }
- pthread_join (im->pthread[i], NULL);
- }
-
- free (im->pthread);
- free (im->ptd);
-
- return 0;
-}
diff --git a/extras/libmemif/examples/icmp_responder-zero-copy-slave/main.c b/extras/libmemif/examples/icmp_responder-zero-copy-slave/main.c
deleted file mode 100644
index 3f871d7f23f..00000000000
--- a/extras/libmemif/examples/icmp_responder-zero-copy-slave/main.c
+++ /dev/null
@@ -1,1270 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 <stdlib.h>
-#include <stdint.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-#include <netinet/if_ether.h>
-#include <net/if_arp.h>
-#include <asm/byteorder.h>
-#include <byteswap.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <errno.h>
-#include <unistd.h>
-#include <signal.h>
-
-#include <time.h>
-
-#include <libmemif.h>
-#include <icmp_proto.h>
-
-#define APP_NAME "ICMP_Responder"
-#define IF_NAME "memif_connection"
-
-#define HEADROOM 0x80 /* 128b */
-#define ENCAP 0x60
-
-#ifdef ICMP_DBG
-#define DBG(...) do { \
- printf (APP_NAME":%s:%d: ", __func__, __LINE__); \
- printf (__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-#define LOG(...) do { \
- if (enable_log) { \
- dprintf (out_fd, __VA_ARGS__); \
- dprintf (out_fd, "\n"); \
- } \
- } while (0)
-#define LOG_FILE "/tmp/memif_time_test.txt"
-#else
-#define DBG(...)
-#define LOG(...)
-#endif
-
-#define INFO(...) do { \
- printf ("INFO: "__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-
-
-/* maximum tx/rx memif buffers */
-#define MAX_MEMIF_BUFS 256
-#define MAX_CONNS 50
-
-int epfd;
-int out_fd;
-uint8_t enable_log;
-
-typedef struct
-{
- uint16_t index;
- /* memif connection handle */
- memif_conn_handle_t conn;
- /* buffers */
- memif_buffer_t *bufs;
- /* allocated tx buffers counter */
- /* number of tx buffers pointing to shared memory */
- uint16_t tx_buf_num;
- /* allocated rx buffers counter */
- /* number of rx buffers pointing to shared memory */
- uint16_t rx_buf_num;
- /* interface ip address */
- uint8_t ip_addr[4];
- uint64_t tx_counter, rx_counter, tx_err_counter;
- uint64_t t_sec, t_nsec;
-} memif_connection_t;
-
-typedef struct
-{
- uint16_t index;
- icmpr_flow_mode_t mode;
- uint64_t packet_count;
- uint16_t sequence;
- uint64_t tx;
- uint8_t ip_daddr[4];
- uint8_t hw_daddr[6];
- struct timespec *start, end;
-} icmpr_flow_t;
-
-memif_connection_t memif_connection[MAX_CONNS];
-long ctx[MAX_CONNS];
-icmpr_flow_t *flow;
-
-/* print details for all memif connections */
-static void
-print_memif_details ()
-{
- memif_details_t md;
- ssize_t buflen;
- char *buf;
- int err, i, e;
- buflen = 2048;
- buf = malloc (buflen);
- printf ("MEMIF DETAILS\n");
- printf ("==============================\n");
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
-
- memset (&md, 0, sizeof (md));
- memset (buf, 0, buflen);
-
- err = memif_get_details (c->conn, &md, buf, buflen);
- if (err != MEMIF_ERR_SUCCESS)
- {
- if (err != MEMIF_ERR_NOCONN)
- INFO ("%s", memif_strerror (err));
- continue;
- }
-
- printf ("interface index: %d\n", i);
-
- printf ("\tinterface ip: %u.%u.%u.%u\n",
- c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
- printf ("\tinterface name: %s\n", (char *) md.if_name);
- printf ("\tapp name: %s\n", (char *) md.inst_name);
- printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
- printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
- printf ("\tid: %u\n", md.id);
- printf ("\tsecret: %s\n", (char *) md.secret);
- printf ("\trole: ");
- if (md.role)
- printf ("slave\n");
- else
- printf ("master\n");
- printf ("\tmode: ");
- switch (md.mode)
- {
- case 0:
- printf ("ethernet\n");
- break;
- case 1:
- printf ("ip\n");
- break;
- case 2:
- printf ("punt/inject\n");
- break;
- default:
- printf ("unknown\n");
- break;
- }
- printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
- printf ("\trx queues:\n");
- for (e = 0; e < md.rx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
- printf ("\t\tring rx mode: %s\n",
- md.rx_queues[e].flags ? "polling" : "interrupt");
- printf ("\t\tring head: %u\n", md.rx_queues[e].head);
- printf ("\t\tring tail: %u\n", md.rx_queues[e].tail);
- printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
- }
- printf ("\ttx queues:\n");
- for (e = 0; e < md.tx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
- printf ("\t\tring rx mode: %s\n",
- md.tx_queues[e].flags ? "polling" : "interrupt");
- printf ("\t\tring head: %u\n", md.tx_queues[e].head);
- printf ("\t\tring tail: %u\n", md.tx_queues[e].tail);
- printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
- }
- printf ("\tlink: ");
- if (md.link_up_down)
- printf ("up\n");
- else
- printf ("down\n");
- }
- free (buf);
-}
-
-int
-add_epoll_fd (int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d added to epoll", fd);
- return 0;
-}
-
-int
-mod_epoll_fd (int fd, uint32_t events)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d modified on epoll", fd);
- return 0;
-}
-
-int
-del_epoll_fd (int fd)
-{
- if (fd < 0)
- {
- DBG ("invalid fd %d", fd);
- return -1;
- }
- struct epoll_event evt;
- memset (&evt, 0, sizeof (evt));
- if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
- {
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
- return -1;
- }
- DBG ("fd %d removed from epoll", fd);
- return 0;
-}
-
-/* informs user about connected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif connected!");
- memif_refill_queue (conn, 0, -1, HEADROOM);
- enable_log = 1;
- return 0;
-}
-
-/* informs user about disconnected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_disconnect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif disconnected!");
- return 0;
-}
-
-/* user needs to watch new fd or stop watching fd that is about to be closed.
- control fd will be modified during connection establishment to minimize CPU usage */
-int
-control_fd_update (int fd, uint8_t events, void *ctx)
-{
- /* convert memif event definitions to epoll events */
- if (events & MEMIF_FD_EVENT_DEL)
- return del_epoll_fd (fd);
-
- uint32_t evt = 0;
- if (events & MEMIF_FD_EVENT_READ)
- evt |= EPOLLIN;
- if (events & MEMIF_FD_EVENT_WRITE)
- evt |= EPOLLOUT;
-
- if (events & MEMIF_FD_EVENT_MOD)
- return mod_epoll_fd (fd, evt);
-
- return add_epoll_fd (fd, evt);
-}
-
-/* called when event is polled on interrupt file descriptor.
- there are packets in shared memory ready to be received */
-/* handle packet processing in rx buffer then enqueue this buffer to tx and transmit */
-int
-on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- uint16_t rx = 0, tx = 0;
- int i = 0; /* rx buffer iterator */
-
- /* loop while there are packets in shm */
- do
- {
- /* receive data from shared memory buffers (dequeue rx buffers) */
- err = memif_rx_burst (c->conn, qid, c->bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- c->rx_counter += rx;
- c->rx_buf_num += rx;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
-
- /* process buffers in place */
- for (i = 0; i < rx; i++)
- {
- resolve_packet2 ((void *) (c->bufs + i)->data,
- &(c->bufs + i)->len, c->ip_addr);
- }
-
- /* enqueue processed buffers to tx ring */
- err = memif_buffer_enq_tx (c->conn, qid, c->bufs, i, &tx);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- c->rx_buf_num -= tx;
- c->tx_buf_num += tx;
- c->tx_err_counter += i - tx;
-
- /* mark memif buffers and shared memory buffers as free */
- err = memif_refill_queue (c->conn, qid, rx, HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
-
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, rx, MAX_MEMIF_BUFS - rx);
-
- /* transmit allocated buffers */
- err = memif_tx_burst (c->conn, qid, c->bufs, rx, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->tx_counter += tx;
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, -1, HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num = 0;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-/* add ethernet encap to packet in rx buffer then enqueue this buffer to tx and transmit */
-int
-on_interrupt0 (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- uint16_t rx = 0, tx = 0;
- int i = 0; /* rx buffer iterator */
-
- /* loop while there are packets in shm */
- do
- {
- /* receive data from shared memory buffers (dequeue rx buffers) */
- err = memif_rx_burst (c->conn, qid, c->bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- c->rx_counter += rx;
- c->rx_buf_num += rx;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
-
- /* process bufers in place */
- for (i = 0; i < rx; i++)
- {
- resolve_packet3 (&c->bufs[i].data, &c->bufs[i].len, c->ip_addr);
- }
- /* enque processed buffers to tx ring */
- err = memif_buffer_enq_tx (c->conn, qid, c->bufs, rx, &tx);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- goto error;
- }
- c->rx_buf_num -= tx;
- c->tx_buf_num += tx;
- c->tx_err_counter += i - tx;
-
- /* mark memif buffers and shared memory buffers as free */
- err = memif_refill_queue (c->conn, qid, rx, HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
-
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, rx, MAX_MEMIF_BUFS - rx);
-
- /* transmit allocated buffers */
- err = memif_tx_burst (c->conn, qid, c->bufs, i, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- goto error;
- }
- c->tx_counter += tx;
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, -1, HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num = 0;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-/* called when event is polled on interrupt file descriptor.
- there are packets in shared memory ready to be received */
-/* dev test modification: handle only ARP requests */
-int
-on_interrupt1 (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- long index = *((long *) private_ctx);
- memif_connection_t *c = &memif_connection[index];
- if (c->index != index)
- {
- INFO ("invalid context: %ld/%u", index, c->index);
- return 0;
- }
-
- int err = MEMIF_ERR_SUCCESS, ret_val;
- int i;
- uint16_t rx, tx;
-
- do
- {
- /* receive data from shared memory buffers */
- err = memif_rx_burst (c->conn, qid, c->bufs, MAX_MEMIF_BUFS, &rx);
- ret_val = err;
- c->rx_buf_num += rx;
- c->rx_counter += rx;
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF))
- {
- INFO ("memif_rx_burst: %s", memif_strerror (err));
- goto error;
- }
-
- for (i = 0; i < rx; i++)
- {
- if (((struct ether_header *) (c->bufs + i)->data)->ether_type ==
- 0x0608)
- {
- /* process data in place */
- resolve_packet2 ((void *) (c->bufs + i)->data,
- &(c->bufs + i)->len, c->ip_addr);
- /* enque buffer to tx ring */
- memif_buffer_enq_tx (c->conn, qid, c->bufs, 1, &tx);
- c->rx_buf_num -= tx;
- memif_tx_burst (c->conn, qid, c->bufs, 1, &tx);
- }
- }
-
- err = memif_refill_queue (c->conn, qid, -1, HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
-
- }
- while (ret_val == MEMIF_ERR_NOBUF);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, -1, HEADROOM);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num = 0;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
-
-int
-icmpr_memif_create (long index, long mode, char *s)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- /* setting memif connection arguments */
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.is_master = mode;
- args.log2_ring_size = 11;
- args.buffer_size = 2048;
- args.num_s2m_rings = 1;
- args.num_m2s_rings = 1;
- strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
- args.mode = 0;
- /* socket filename is not specified, because this app is supposed to
- connect to VPP over memif. so default socket filename will be used */
- /* default socketfile = /run/vpp/memif.sock */
-
- args.interface_id = index;
- /* last argument for memif_create (void * private_ctx) is used by user
- to identify connection. this context is returned with callbacks */
- int err;
- /* default interrupt */
- if (s == NULL)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else
- {
- if (strncmp (s, "0", 1) == 0)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt0,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else if (strncmp (s, "1", 1) == 0)
- {
- err = memif_create (&c->conn,
- &args, on_connect, on_disconnect, on_interrupt1,
- &ctx[index]);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
- }
- }
- else
- {
- INFO ("Unknown interrupt descriptor");
- goto done;
- }
- }
-
- c->index = index;
- /* alloc memif buffers */
- c->rx_buf_num = 0;
- c->tx_buf_num = 0;
- c->bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
-
- c->ip_addr[0] = 192;
- c->ip_addr[1] = 168;
- c->ip_addr[2] = c->index + 1;
- c->ip_addr[3] = 2;
-
- c->tx_err_counter = c->tx_counter = c->rx_counter = 0;
-
-done:
- return 0;
-}
-
-int
-icmpr_memif_delete (long index)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- if (c->bufs)
- free (c->bufs);
- c->bufs = NULL;
- c->tx_buf_num = 0;
- c->rx_buf_num = 0;
-
- int err;
- /* disconnect then delete memif connection */
- err = memif_delete (&c->conn);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
- if (c->conn != NULL)
- INFO ("memif delete fail");
- return 0;
-}
-
-void
-print_help ()
-{
- printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
-#ifdef ICMP_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("==============================\n");
- printf ("libmemif version: %s", LIBMEMIF_VERSION);
-#ifdef MEMIF_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("memif version: %d\n", memif_get_version ());
- printf ("commands:\n");
- printf ("\thelp - prints this help\n");
- printf ("\texit - exit app\n");
- printf
- ("\tconn <index> <mode> [<interrupt-desc>] - create memif. index is also used as interface id, mode 0 = slave 1 = master, interrupt-desc none = default 0 = if ring is full wait 1 = handle only ARP requests\n");
- printf ("\tdel <index> - delete memif\n");
- printf ("\tshow - show connection details\n");
- printf ("\tip-set <index> <ip-addr> - set interface ip address\n");
- printf
- ("\trx-mode <index> <qid> <polling|interrupt> - set queue rx mode\n");
- printf ("\tsh-count - print counters\n");
- printf ("\tcl-count - clear counters\n");
- printf
- ("\tsend <index> <tx> <ip> <mac> - send icmp, ommit mac to transmit on ip layer\n");
-}
-
-int
-icmpr_free ()
-{
- /* application cleanup */
- int err;
- long i;
- if (out_fd > 0)
- close (out_fd);
- out_fd = -1;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn)
- icmpr_memif_delete (i);
- }
-
- err = memif_cleanup ();
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
-
- return 0;
-}
-
-int
-icmpr_set_ip (long index, char *ip)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
- if (c->conn == NULL)
- {
- INFO ("no connection at index %ld", index);
- return 0;
- }
-
- char *end;
- char *ui;
- uint8_t tmp[4];
- ui = strtok (ip, ".");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 10);
-
- c->ip_addr[0] = tmp[0];
- c->ip_addr[1] = tmp[1];
- c->ip_addr[2] = tmp[2];
- c->ip_addr[3] = tmp[3];
-
- INFO ("memif %ld ip address set to %u.%u.%u.%u",
- index, c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
-
- return 0;
-
-error:
- INFO ("invalid ip address");
- return 0;
-}
-
-int
-icmpr_set_rx_mode (long index, long qid, char *mode)
-{
- if (index >= MAX_CONNS)
- {
- INFO ("connection array overflow");
- return 0;
- }
- if (index < 0)
- {
- INFO ("don't even try...");
- return 0;
- }
- memif_connection_t *c = &memif_connection[index];
-
- if (c->conn == NULL)
- {
- INFO ("no connection at index %ld", index);
- return 0;
- }
-
- if (strncmp (mode, "interrupt", 9) == 0)
- {
- memif_set_rx_mode (c->conn, MEMIF_RX_MODE_INTERRUPT, qid);
- }
-
- else if (strncmp (mode, "polling", 7) == 0)
- {
- memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, qid);
- }
- else
- INFO ("expected rx mode <interrupt|polling>");
- return 0;
-}
-
-void
-icmpr_print_counters ()
-{
- int i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn == NULL)
- continue;
- printf ("===============================\n");
- printf ("interface index: %d\n", c->index);
- printf ("\trx: %lu\n", c->rx_counter);
- printf ("\ttx: %lu\n", c->tx_counter);
- printf ("\ttx_err: %lu\n", c->tx_err_counter);
- printf ("\tts: %lus %luns\n", c->t_sec, c->t_nsec);
- }
-}
-
-void
-icmpr_reset_counters ()
-{
- int i;
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection_t *c = &memif_connection[i];
- if (c->conn == NULL)
- continue;
- c->t_sec = c->t_nsec = c->tx_err_counter = c->tx_counter =
- c->rx_counter = 0;
- }
-}
-
-void
-icmpr_send_proc ()
-{
- memif_connection_t *c = &memif_connection[flow->index];
- if (c->conn == NULL)
- {
- INFO ("No connection at index %d. Stopping flow...\n", flow->index);
- goto stop_flow;
- }
- uint16_t tx, i;
- int err = MEMIF_ERR_SUCCESS;
-
- if (!flow->start)
- {
- flow->start = malloc (sizeof (struct timespec));
- memset (flow->start, 0, sizeof (struct timespec));
- timespec_get (flow->start, TIME_UTC);
- }
-
- i = 0;
- err = memif_buffer_alloc (c->conn, 0, c->bufs,
- MAX_MEMIF_BUFS >
- flow->packet_count ? flow->packet_count :
- MAX_MEMIF_BUFS, &tx, 64);
- if ((err != MEMIF_ERR_SUCCESS) && (err != MEMIF_ERR_NOBUF_RING))
- {
- INFO ("memif_buffer_alloc: %s Stopping flow...\n",
- memif_strerror (err));
- goto stop_flow;
- }
- c->tx_buf_num += tx;
-
- while (tx)
- {
- generate_packet2 ((void *) c->bufs[i].data,
- &c->bufs[i].len, c->ip_addr,
- flow->ip_daddr, flow->hw_daddr, (flow->sequence)++,
- flow->mode);
- i++;
- tx--;
- }
- err = memif_tx_burst (c->conn, 0, c->bufs, i, &tx);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_tx_burst: %s Stopping flow...\n", memif_strerror (err));
- goto stop_flow;
- }
- c->tx_buf_num -= tx;
- c->tx_counter += tx;
- flow->tx += tx;
- flow->packet_count -= tx;
-
- if (flow->packet_count == 0)
- {
- timespec_get (&flow->end, TIME_UTC);
- INFO ("Flow finished!");
- INFO ("Flow length: %lu", flow->tx);
- uint64_t t1 = flow->end.tv_sec - flow->start->tv_sec;
- uint64_t t2;
- if (flow->end.tv_nsec > flow->start->tv_nsec)
- {
- t2 = flow->end.tv_nsec - flow->start->tv_nsec;
- }
- else
- {
- t2 = flow->start->tv_nsec - flow->end.tv_nsec;
- t1--;
- }
- c->t_sec = t1;
- c->t_nsec = t2;
- INFO ("Flow time: %lus %luns", t1, t2);
- double tmp = t1;
- tmp += t2 / 1e+9;
- tmp = flow->tx / tmp;
- INFO ("Average pps: %f", tmp);
- INFO ("Stopping flow...");
- goto stop_flow;
- }
-
- return;
-
-stop_flow:
- if (flow)
- {
- if (flow->start)
- free (flow->start);
- free (flow);
- }
- flow = NULL;
- return;
-}
-
-int
-icmpr_send (long index, long packet_num, char *input)
-{
- if (flow)
- {
- printf ("only one flow allowed\n");
- return 0;
- }
-
- memif_connection_t *c = &memif_connection[index];
- char *end;
- char *ui;
- uint8_t tmp[6];
- if (c->conn == NULL)
- return -1;
-
- flow = malloc (sizeof (icmpr_flow_t));
- flow->index = index;
- flow->packet_count = packet_num;
- flow->sequence = 0;
- flow->tx = 0;
- flow->start = NULL;
- memset (&flow->end, 0, sizeof (struct timespec));
-
- INFO ("packet count: %lu", flow->packet_count);
- printf ("%s\n", input);
-
- ui = strtok (input, ".");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 10);
-
- ui = strtok (NULL, ".");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 10);
-
- flow->ip_daddr[0] = tmp[0];
- flow->ip_daddr[1] = tmp[1];
- flow->ip_daddr[2] = tmp[2];
- flow->ip_daddr[3] = tmp[3];
-
- ui = strtok (NULL, " ");
- if (ui == NULL)
- {
- flow->mode = ICMPR_FLOW_MODE_IP;
- return 0;
- }
-
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[0] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[1] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[2] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[3] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[4] = strtol (ui, &end, 16);
- ui = strtok (NULL, ":");
- if (ui == NULL)
- goto error;
- tmp[5] = strtol (ui, &end, 16);
-
- flow->hw_daddr[0] = tmp[0];
- flow->hw_daddr[1] = tmp[1];
- flow->hw_daddr[2] = tmp[2];
- flow->hw_daddr[3] = tmp[3];
- flow->hw_daddr[4] = tmp[4];
- flow->hw_daddr[5] = tmp[5];
-
- flow->mode = ICMPR_FLOW_MODE_ETH;
-
- return 0;
-
-error:
- INFO ("Invalid input\n");
- if (flow)
- free (flow);
- flow = NULL;
- return 0;
-}
-
-int
-user_input_handler ()
-{
- char *in = (char *) malloc (256);
- char *ui = fgets (in, 256, stdin);
- char *end;
- long a;
- if (in[0] == '\n')
- goto done;
- ui = strtok (in, " ");
- if (strncmp (ui, "exit", 4) == 0)
- {
- free (in);
- icmpr_free ();
- exit (EXIT_SUCCESS);
- }
- else if (strncmp (ui, "help", 4) == 0)
- {
- print_help ();
- goto done;
- }
- else if (strncmp (ui, "conn", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_create (a, strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected mode <0|1>");
- goto done;
- }
- else if (strncmp (ui, "del", 3) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_memif_delete (strtol (ui, &end, 10));
- else
- INFO ("expected id");
- goto done;
- }
- else if (strncmp (ui, "show", 4) == 0)
- {
- print_memif_details ();
- goto done;
- }
- else if (strncmp (ui, "ip-set", 6) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_set_ip (strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected id");
- goto done;
- }
- else if (strncmp (ui, "rx-mode", 7) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_set_rx_mode (a, strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected qid");
- goto done;
- }
- else if (strncmp (ui, "sh-count", 8) == 0)
- {
- icmpr_print_counters ();
- }
- else if (strncmp (ui, "cl-count", 8) == 0)
- {
- icmpr_reset_counters ();
- }
- else if (strncmp (ui, "send", 4) == 0)
- {
- ui = strtok (NULL, " ");
- if (ui != NULL)
- a = strtol (ui, &end, 10);
- else
- {
- INFO ("expected id");
- goto done;
- }
- ui = strtok (NULL, " ");
- if (ui != NULL)
- icmpr_send (a, strtol (ui, &end, 10), strtok (NULL, " "));
- else
- INFO ("expected count");
- goto done;
- }
- else
- {
- INFO ("unknown command: %s", ui);
- goto done;
- }
-
- return 0;
-done:
- free (in);
- return 0;
-}
-
-int
-poll_event (int timeout)
-{
- struct epoll_event evt;
- int app_err = 0, memif_err = 0, en = 0;
- uint32_t events = 0;
- struct timespec start, end;
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
- sigset_t sigset;
- sigemptyset (&sigset);
- en = epoll_pwait (epfd, &evt, 1, timeout, &sigset);
- /* id event polled */
- timespec_get (&start, TIME_UTC);
- if (en < 0)
- {
- DBG ("epoll_pwait: %s", strerror (errno));
- return -1;
- }
- if (en > 0)
- {
- /* this app does not use any other file descriptors than stds and memif control fds */
- if (evt.data.fd > 2)
- {
- /* event of memif control fd */
- /* convert epoll events to memif events */
- if (evt.events & EPOLLIN)
- events |= MEMIF_FD_EVENT_READ;
- if (evt.events & EPOLLOUT)
- events |= MEMIF_FD_EVENT_WRITE;
- if (evt.events & EPOLLERR)
- events |= MEMIF_FD_EVENT_ERROR;
- memif_err = memif_control_fd_handler (evt.data.fd, events);
- if (memif_err != MEMIF_ERR_SUCCESS)
- INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err));
- }
- else if (evt.data.fd == 0)
- {
- app_err = user_input_handler ();
- }
- else
- {
- DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd);
- }
- }
-
- timespec_get (&end, TIME_UTC);
- LOG ("interrupt: %ld", end.tv_nsec - start.tv_nsec);
-
- if ((app_err < 0) || (memif_err < 0))
- {
- if (app_err < 0)
- DBG ("user input handler error");
- if (memif_err < 0)
- DBG ("memif control fd handler error");
- return -1;
- }
-
- return 0;
-}
-
-int
-main ()
-{
- epfd = epoll_create (1);
- add_epoll_fd (0, EPOLLIN);
-
- flow = NULL;
-
-#ifdef LOG_FILE
- remove (LOG_FILE);
- enable_log = 0;
-
- out_fd = open (LOG_FILE, O_WRONLY | O_CREAT, S_IRWXO);
- if (out_fd < 0)
- INFO ("Error opening log file: %s", strerror (errno));
-#endif /* LOG_FILE */
-
- /* initialize memory interface */
- int err, i;
- /* if valid callback is passed as argument, fd event polling will be done by user
- all file descriptors and events will be passed to user in this callback */
- /* if callback is set to NULL libmemif will handle fd event polling */
- err = memif_init (control_fd_update, APP_NAME, NULL, NULL, NULL);
- if (err != MEMIF_ERR_SUCCESS)
- {
- INFO ("memif_init: %s", memif_strerror (err));
- icmpr_free ();
- exit (-1);
- }
-
- for (i = 0; i < MAX_CONNS; i++)
- {
- memif_connection[i].conn = NULL;
- ctx[i] = i;
- }
-
- print_help ();
-
- /* main loop */
- while (1)
- {
- if (poll_event (0) < 0)
- {
- DBG ("poll_event error!");
- }
- if (flow)
- {
- icmpr_send_proc ();
- }
- }
-}
diff --git a/extras/libmemif/examples/icmp_responder/main.c b/extras/libmemif/examples/icmp_responder/main.c
index 517512340e0..d70ecb5647e 100644
--- a/extras/libmemif/examples/icmp_responder/main.c
+++ b/extras/libmemif/examples/icmp_responder/main.c
@@ -1,6 +1,6 @@
/*
*------------------------------------------------------------------
- * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Copyright (c) 2020 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:
@@ -16,187 +16,71 @@
*/
#include <stdlib.h>
-#include <stdint.h>
-#include <net/if.h>
#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
-#include <netdb.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-#include <netinet/if_ether.h>
-#include <net/if_arp.h>
-#include <asm/byteorder.h>
-#include <byteswap.h>
#include <string.h>
-#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
-#include <signal.h>
+#include <getopt.h>
#include <libmemif.h>
-#include <icmp_proto.h>
-
-#define APP_NAME "ICMP_Responder"
-#define IF_NAME "memif_connection"
-
-
-#ifdef ICMP_DBG
-#define DBG(...) do { \
- printf (APP_NAME":%s:%d: ", __func__, __LINE__); \
- printf (__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
-#else
-#define DBG(...)
-#endif
-
-#define INFO(...) do { \
- printf ("INFO: "__VA_ARGS__); \
- printf ("\n"); \
- } while (0)
+#include <common.h>
-/* maximum tx/rx memif buffers */
-#define MAX_MEMIF_BUFS 256
+#define APP_NAME "icmp_responder_example"
-typedef struct
-{
- uint16_t index;
- /* memif conenction handle */
- memif_conn_handle_t conn;
- /* transmit queue id */
- uint16_t tx_qid;
- /* tx buffers */
- memif_buffer_t *tx_bufs;
- /* allocated tx buffers counter */
- /* number of tx buffers pointing to shared memory */
- uint16_t tx_buf_num;
- /* rx buffers */
- memif_buffer_t *rx_bufs;
- /* allcoated rx buffers counter */
- /* number of rx buffers pointing to shared memory */
- uint16_t rx_buf_num;
- /* interface ip address */
- uint8_t ip_addr[4];
-} memif_connection_t;
+#define IF_NAME "libmemif0"
+#define IF_ID 0
+#define SOCKET_PATH "/run/vpp/memif.sock"
+const uint8_t HW_ADDR[6] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa };
+const uint8_t IP_ADDR[4] = { 192, 168, 1, 1 };
-memif_connection_t memif_connection;
+memif_connection_t intf;
int epfd;
-static void
-print_memif_details ()
+/* informs user about connected status. private_ctx is used by user to identify
+ * connection */
+int
+on_connect (memif_conn_handle_t conn, void *private_ctx)
{
- memif_connection_t *c = &memif_connection;
- printf ("MEMIF DETAILS\n");
- printf ("==============================\n");
+ INFO ("memif connected!");
+ int err;
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
- memif_details_t md;
- memset (&md, 0, sizeof (md));
- ssize_t buflen = 2048;
- char *buf = malloc (buflen);
- memset (buf, 0, buflen);
- int err, e;
+ c->is_connected = 1;
+ alloc_memif_buffers (c);
- err = memif_get_details (c->conn, &md, buf, buflen);
+ err = memif_refill_queue (conn, 0, -1, c->headroom_size);
if (err != MEMIF_ERR_SUCCESS)
{
- INFO ("%s", memif_strerror (err));
- if (err == MEMIF_ERR_NOCONN)
- {
- free (buf);
- return;
- }
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ return err;
}
- printf ("\tinterface name: %s\n", (char *) md.if_name);
- printf ("\tapp name: %s\n", (char *) md.inst_name);
- printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
- printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
- printf ("\tid: %u\n", md.id);
- printf ("\tsecret: %s\n", (char *) md.secret);
- printf ("\trole: ");
- if (md.role)
- printf ("slave\n");
- else
- printf ("master\n");
- printf ("\tmode: ");
- switch (md.mode)
- {
- case 0:
- printf ("ethernet\n");
- break;
- case 1:
- printf ("ip\n");
- break;
- case 2:
- printf ("punt/inject\n");
- break;
- default:
- printf ("unknown\n");
- break;
- }
- printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
- printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
- printf ("\trx queues:\n");
- for (e = 0; e < md.rx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
- printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
- }
- printf ("\ttx queues:\n");
- for (e = 0; e < md.tx_queues_num; e++)
- {
- printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid);
- printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
- printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
- }
- printf ("\tlink: ");
- if (md.link_up_down)
- printf ("up\n");
- else
- printf ("down\n");
-
- free (buf);
-}
+ print_memif_details (c);
-/* informs user about connected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
-int
-on_connect (memif_conn_handle_t conn, void *private_ctx)
-{
- INFO ("memif connected!");
return 0;
}
-/* informs user about disconnected status. private_ctx is used by user to identify connection
- (multiple connections WIP) */
+/* informs user about disconnected status. private_ctx is used by user to
+ * identify connection */
int
on_disconnect (memif_conn_handle_t conn, void *private_ctx)
{
INFO ("memif disconnected!");
- return 0;
-}
-int
-icmpr_memif_delete ()
-{
- int err;
- /* disconnect then delete memif connection */
- err = memif_delete (&(&memif_connection)->conn);
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+
+ c->is_connected = 0;
+ free_memif_buffers (c);
+
+ /* stop event polling thread */
+ int err = memif_cancel_poll_event (memif_get_socket_handle (conn));
if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
+ INFO ("We are doomed...");
+
return 0;
}
@@ -209,209 +93,163 @@ print_help ()
#endif
printf ("\n");
printf ("==============================\n");
- printf ("libmemif version: %s", LIBMEMIF_VERSION);
-#ifdef MEMIF_DBG
- printf (" (debug)");
-#endif
- printf ("\n");
- printf ("memif version: %d\n", memif_get_version ());
- printf ("\tuse CTRL+C to exit\n");
+ print_version ();
+ printf ("==============================\n");
+ printf (
+ "In this example, memif endpoint connects to an external application.\n");
+ printf (
+ "The example application can resolve ARP and reply to ICMPv4 packets.\n");
+ printf ("The program will exit once the interface is disconnected.\n");
+ printf ("==============================\n");
+ printf ("Usage: icmp_responder [OPTIONS]\n\n");
+ printf ("Options:\n");
+ printf ("\t-r\tInterface role <slave|master>. Default: slave\n");
+ printf ("\t-s\tSocket path. Supports abstract socket using @ before the "
+ "path. Default: /run/vpp/memif.sock\n");
+ printf ("\t-b\tBuffer Size. Default: 2048\n");
+ printf ("\t-h\tHeadroom Size. Default: 0\n");
+ printf ("\t-i\tInterface id. Default: 0\n");
+ printf ("\t-a\tIPv4 address. Default: 192.168.1.1\n");
+ printf ("\t-m\tMac address. Default: aa:aa:aa:aa:aa:aa\n");
+ printf ("\t-?\tShow help and exit.\n");
+ printf ("\t-v\tShow libmemif and memif version information and exit.\n");
}
int
-icmpr_buffer_alloc (long n, uint16_t qid)
+main (int argc, char *argv[])
{
- memif_connection_t *c = &memif_connection;
- int err;
- uint16_t r;
- /* set data pointer to shared memory and set buffer_len to shared memory buffer len */
- err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, n, &r, 0);
- if (err != MEMIF_ERR_SUCCESS)
+ memif_socket_args_t memif_socket_args = { 0 };
+ memif_socket_handle_t memif_socket;
+ memif_conn_args_t memif_conn_args = { 0 };
+ int opt, err, ret = 0;
+ uint8_t is_master = 0;
+ char socket_path[108];
+ int id = IF_ID;
+
+ strncpy (socket_path, SOCKET_PATH, sizeof (SOCKET_PATH));
+
+ /* prepare the private data */
+ memset (&intf, 0, sizeof (intf));
+ intf.packet_handler = icmp_packet_handler;
+ memcpy (intf.ip_addr, IP_ADDR, 4);
+ memcpy (intf.hw_addr, HW_ADDR, 6);
+
+ while ((opt = getopt (argc, argv, "r:s:b:h:i:a:m:?v")) != -1)
{
- INFO ("memif_buffer_alloc: %s", memif_strerror (err));
- c->tx_buf_num += r;
- return -1;
+ switch (opt)
+ {
+ case 'r':
+ if (strncmp (optarg, "master", sizeof (optarg)) == 0)
+ {
+ is_master = 1;
+ }
+ else if (strncmp (optarg, "slave", sizeof (optarg)) == 0)
+ {
+ is_master = 0;
+ }
+ else
+ {
+ INFO ("Invalid role value: '%s'", optarg);
+ return -1;
+ }
+ break;
+ case 's':
+ sprintf (socket_path, "%s", optarg);
+ break;
+ case 'b':
+ intf.buffer_size = atoi (optarg);
+ break;
+ case 'h':
+ intf.headroom_size = atoi (optarg);
+ break;
+ case 'i':
+ id = atoi (optarg);
+ break;
+ case 'a':
+ if (parse_ip4 (optarg, intf.ip_addr) != 0)
+ {
+ INFO ("Invalid ipv4 address: %s", optarg);
+ return -1;
+ }
+ break;
+ case 'm':
+ if (parse_mac (optarg, intf.hw_addr) != 0)
+ {
+ INFO ("Invalid mac address: %s", optarg);
+ return -1;
+ }
+ break;
+ case '?':
+ print_help ();
+ return 0;
+ case 'v':
+ print_version ();
+ return 0;
+ }
}
- c->tx_buf_num += r;
- DBG ("allocated %d/%ld buffers, %u free buffers", r, n,
- MAX_MEMIF_BUFS - c->tx_buf_num);
- return 0;
-}
-int
-icmpr_tx_burst (uint16_t qid)
-{
- memif_connection_t *c = &memif_connection;
- int err;
- uint16_t r;
- /* inform peer memif interface about data in shared memory buffers */
- /* mark memif buffers as free */
- err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_tx_burst: %s", memif_strerror (err));
- DBG ("tx: %d/%u", r, c->tx_buf_num);
- c->tx_buf_num -= r;
- return 0;
-}
+ /** Create memif socket
+ *
+ * Interfaces are internally stored in a database referenced by memif socket.
+ */
+ sprintf (memif_socket_args.path, "%s", socket_path);
+ /* Set application name */
+ strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME));
-int
-icmpr_free ()
-{
- /* application cleanup */
- int err;
- memif_connection_t *c = &memif_connection;
- free (c->tx_bufs);
- c->tx_bufs = NULL;
- free (c->rx_bufs);
- c->rx_bufs = NULL;
+ /* configure autoconnect timer */
+ if (is_master == 0)
+ {
+ memif_socket_args.connection_request_timer.it_value.tv_sec = 2;
+ memif_socket_args.connection_request_timer.it_value.tv_nsec = 0;
+ memif_socket_args.connection_request_timer.it_interval.tv_sec = 2;
+ memif_socket_args.connection_request_timer.it_interval.tv_nsec = 0;
+ }
- err = memif_cleanup ();
+ err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_delete: %s", memif_strerror (err));
-
- return 0;
-}
-
-void
-icmpr_exit (int sig)
-{
- printf ("\n");
- icmpr_memif_delete ();
- icmpr_free ();
- exit (EXIT_SUCCESS);
-}
-
-/* called when event is polled on interrupt file descriptor.
- there are packets in shared memory ready to be received */
-int
-on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
-{
- DBG ("interrupted");
- memif_connection_t *c = &memif_connection;
- int err;
- uint16_t rx;
- /* receive data from shared memory buffers */
- err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
- c->rx_buf_num += rx;
-
- DBG ("received %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
-
- if (icmpr_buffer_alloc (rx, c->tx_qid) < 0)
{
- INFO ("buffer_alloc error");
+ INFO ("memif_create_socket: %s", memif_strerror (err));
goto error;
}
- int i;
- for (i = 0; i < rx; i++)
- {
- resolve_packet ((void *) (c->rx_bufs + i)->data,
- (c->rx_bufs + i)->len,
- (void *) (c->tx_bufs + i)->data,
- &(c->tx_bufs + i)->len, c->ip_addr);
- }
- /* mark memif buffers and shared memory buffers as free */
- err = memif_refill_queue (c->conn, qid, rx, 0);
- /*
- * In this example we can assert that c->conn points to valid connection
- * and 'rx <= c->rx_buf_num'.
+ /** Create memif interfaces
+ *
+ * Both interaces are assigned the same socket and same id to create a
+ * loopback.
*/
- c->rx_buf_num -= rx;
-
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
-
- icmpr_tx_burst (c->tx_qid);
-
- return 0;
-
-error:
- err = memif_refill_queue (c->conn, qid, rx, 0);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_buffer_free: %s", memif_strerror (err));
- c->rx_buf_num -= rx;
- DBG ("freed %d buffers. %u/%u alloc/free buffers",
- rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
- return 0;
-}
+ if (intf.buffer_size)
+ memif_conn_args.buffer_size = intf.buffer_size;
+ else
+ intf.buffer_size = 2048;
-int
-icmpr_memif_create (int is_master)
-{
- /* setting memif connection arguments */
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.is_master = is_master;
- args.log2_ring_size = 10;
- args.buffer_size = 2048;
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
- strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
- args.mode = 0;
- /* socket filename is not specified, because this app is supposed to
- connect to VPP over memif. so default socket filename will be used */
- /* default socketfile = /run/vpp/memif.sock */
+ memif_conn_args.socket = memif_socket;
+ memif_conn_args.interface_id = id;
+ strncpy (memif_conn_args.interface_name, IF_NAME,
+ sizeof (memif_conn_args.interface_name));
+ memif_conn_args.is_master = is_master;
- args.interface_id = 0;
- /* last argument for memif_create (void * private_ctx) is used by user
- to identify connection. this context is returned with callbacks */
- int err = memif_create (&(&memif_connection)->conn,
- &args, on_connect, on_disconnect, on_interrupt,
- NULL);
+ err =
+ memif_create (&intf.conn, &memif_conn_args, on_connect, on_disconnect,
+ is_master ? responder : responder_zero_copy, (void *) &intf);
if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_create: %s", memif_strerror (err));
- return 0;
-}
-
-int
-main (int argc, char *argv[])
-{
- memif_connection_t *c = &memif_connection;
-
- signal (SIGINT, icmpr_exit);
-
- /* initialize global memif connection handle */
- c->conn = NULL;
- if (argc == 1)
- c->tx_qid = 0;
- else
{
- char *end;
- c->tx_qid = strtol (argv[1], &end, 10);
+ INFO ("memif_create_socket: %s", memif_strerror (err));
+ return err;
}
- INFO ("tx qid: %u", c->tx_qid);
- /* alloc memif buffers */
- c->rx_buf_num = 0;
- c->rx_bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- c->tx_buf_num = 0;
- c->tx_bufs =
- (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
- c->ip_addr[0] = 192;
- c->ip_addr[1] = 168;
- c->ip_addr[2] = 1;
- c->ip_addr[3] = 2;
- /* initialize memory interface */
- int err;
- /* if valid callback is passed as argument, fd event polling will be done by user
- all file descriptors and events will be passed to user in this callback */
- /* if callback is set to NULL libmemif will handle fd event polling */
- err = memif_init (NULL, APP_NAME, NULL, NULL, NULL);
- if (err != MEMIF_ERR_SUCCESS)
- INFO ("memif_init: %s", memif_strerror (err));
-
- print_help ();
-
- icmpr_memif_create (0);
- print_memif_details ();
- /* main loop */
- while (1)
+ do
{
- if (memif_poll_event (-1) < 0)
- {
- DBG ("poll_event error!");
- }
+ err = memif_poll_event (memif_socket, -1);
}
+ while (err == MEMIF_ERR_SUCCESS);
+
+ return 0;
+
+error:
+ ret = -1;
+done:
+ free_memif_buffers (&intf);
+ memif_delete (&intf.conn);
+ memif_delete_socket (&memif_socket);
+ return ret;
}
diff --git a/extras/libmemif/examples/loopback/main.c b/extras/libmemif/examples/loopback/main.c
new file mode 100644
index 00000000000..e68f69ddaf0
--- /dev/null
+++ b/extras/libmemif/examples/loopback/main.c
@@ -0,0 +1,307 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2020 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 <stdlib.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <libmemif.h>
+#include <common.h>
+
+#define APP_NAME "loopback_example"
+#define IF0_NAME "lo0"
+#define IF1_NAME "lo1"
+
+memif_connection_t intf0, intf1;
+int is_reverse;
+int epfd;
+
+int
+packet_generator (memif_connection_t *c, uint16_t num_pkts)
+{
+ int i, bi = 0;
+ memif_buffer_t *mb;
+
+ for (i = 0; (i < num_pkts) && (bi < c->tx_buf_num); i++)
+ {
+ mb = &c->tx_bufs[bi++];
+ memset (mb->data, 1, mb->len);
+ }
+
+ return 0;
+}
+
+/* informs user about connected status. private_ctx is used by user to identify
+ * connection */
+int
+on_connect (memif_conn_handle_t conn, void *private_ctx)
+{
+ INFO ("memif connected!");
+ int err;
+
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+
+ c->is_connected = 1;
+ alloc_memif_buffers (c);
+
+ err = memif_refill_queue (conn, 0, -1, 0);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ return err;
+ }
+
+ print_memif_details (c);
+
+ /* Once both interfaces are connected send a test packet, master -> slave.
+ * Slave will use zero-copy method to reply the same pakcet back.
+ * (Configured by assigning responder_zero_copy as on_interrupt callback.)
+ */
+ if ((intf0.is_connected == 1) && (intf1.is_connected == 1))
+ {
+ send_packets (is_reverse ? &intf1 : &intf0, 0, packet_generator, 1,
+ 2048);
+ }
+
+ return 0;
+}
+
+/* informs user about disconnected status. private_ctx is used by user to
+ * identify connection */
+int
+on_disconnect (memif_conn_handle_t conn, void *private_ctx)
+{
+ INFO ("memif disconnected!");
+
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+
+ c->is_connected = 0;
+ free_memif_buffers (c);
+
+ /* stop event polling thread */
+ int err = memif_cancel_poll_event (memif_get_socket_handle (conn));
+ if (err != MEMIF_ERR_SUCCESS)
+ INFO ("We are doomed...");
+
+ return 0;
+}
+
+int
+verify_packet (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
+{
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+ int err;
+ void *want;
+
+ err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("meif_rx_burst: %s", memif_strerror (err));
+ return err;
+ }
+
+ want = malloc (c->rx_bufs[0].len);
+ if (want == NULL)
+ {
+ INFO ("Out of memory");
+ goto done;
+ }
+
+ memset (want, 1, c->rx_bufs[0].len);
+
+ err = memcmp (c->rx_bufs[0].data, want, c->rx_bufs[0].len);
+ if (err != 0)
+ {
+ INFO ("Received malformed data. ret: %d", err);
+ }
+ else
+ {
+ INFO ("Received correct data.");
+ }
+
+done:
+ err = memif_refill_queue (conn, qid, c->rx_buf_num, 0);
+ if (err != MEMIF_ERR_SUCCESS)
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+
+ /* stop polling and exit the program */
+ INFO ("Stopping the program");
+ err = memif_cancel_poll_event (memif_get_socket_handle (conn));
+ if (err != MEMIF_ERR_SUCCESS)
+ INFO ("We are doomed...");
+
+ return err;
+}
+
+int
+create_memif_interface (memif_socket_handle_t memif_socket,
+ const char *if_name, int id, uint8_t is_master,
+ memif_connection_t *ctx)
+{
+ memif_conn_args_t memif_conn_args = { 0 };
+ int err;
+
+ memif_conn_args.socket = memif_socket;
+ memif_conn_args.interface_id = id;
+ strncpy (memif_conn_args.interface_name, if_name,
+ sizeof (memif_conn_args.interface_name));
+ memif_conn_args.is_master = is_master;
+
+ err = memif_create (&ctx->conn, &memif_conn_args, on_connect, on_disconnect,
+ is_master ? verify_packet : responder_zero_copy,
+ (void *) ctx);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_create_socket: %s", memif_strerror (err));
+ return err;
+ }
+
+ return 0;
+}
+
+void
+print_help ()
+{
+ printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
+#ifdef ICMP_DBG
+ printf (" (debug)");
+#endif
+ printf ("\n");
+ printf ("==============================\n");
+ printf ("libmemif version: %s", LIBMEMIF_VERSION);
+#ifdef MEMIF_DBG
+ printf (" (debug)");
+#endif
+ printf ("\n");
+
+ printf ("memif version: %s\n", memif_get_version_str ());
+ printf ("==============================\n");
+ printf ("In this example, two memif endpoints are connected to create a "
+ "loopback.\n");
+ printf ("Once connected, a test packet is sent out the memif master "
+ "interface to\n");
+ printf (
+ "the memif slave interface, which replies with the same packet in a\n");
+ printf ("zero-copy way.\n");
+ printf (
+ "In reverse mode, the packet is sent from the slave interface and is\n");
+ printf ("looped back by the master interface.\n");
+ printf ("==============================\n");
+ printf ("Usage: loopback [OPTIONS]\n\n");
+ printf ("Options:\n");
+ printf ("\t-r\tReverse mode, verification packet is sent by slave.\n");
+ printf ("\t-?\tShow help and exit.\n");
+ printf ("\t-v\tShow libmemif and memif version information and exit.\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+ memif_socket_args_t memif_socket_args = { 0 };
+ memif_socket_handle_t memif_socket;
+ int opt, err, ret = 0;
+ is_reverse = 0;
+
+ while ((opt = getopt (argc, argv, "r?v")) != -1)
+ {
+ switch (opt)
+ {
+ case 'r':
+ is_reverse = 1;
+ break;
+ case '?':
+ print_help ();
+ return 0;
+ case 'v':
+ print_version ();
+ return 0;
+ }
+ }
+
+ /** Create memif socket
+ *
+ * Interfaces are internally stored in a database referenced by memif socket.
+ */
+ /* Abstract socket supported */
+ memif_socket_args.path[0] = '@';
+ strncpy (memif_socket_args.path + 1, APP_NAME, strlen (APP_NAME));
+ /* Set application name */
+ strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME));
+
+ err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_create_socket: %s", memif_strerror (err));
+ goto error;
+ }
+
+ /** Create memif interfaces
+ *
+ * Both interaces are assigned the same socket and same id to create a
+ * loopback.
+ */
+
+ /* prepare the private data */
+ memset (&intf0, 0, sizeof (intf0));
+ memset (&intf1, 0, sizeof (intf1));
+ if (is_reverse)
+ {
+ intf0.packet_handler = basic_packet_handler;
+ }
+ else
+ {
+ intf1.packet_handler = basic_packet_handler;
+ }
+
+ err =
+ create_memif_interface (memif_socket, IF0_NAME, 0, /* master */ 1, &intf0);
+ if (err != 0)
+ {
+ goto error;
+ }
+
+ err =
+ create_memif_interface (memif_socket, IF1_NAME, 0, /* slave */ 0, &intf1);
+ if (err != 0)
+ {
+ goto error;
+ }
+
+ do
+ {
+ err = memif_poll_event (memif_socket, -1);
+ }
+ while (err == MEMIF_ERR_SUCCESS);
+
+ return 0;
+
+error:
+ ret = -1;
+done:
+ free_memif_buffers (&intf0);
+ free_memif_buffers (&intf1);
+ memif_delete (&intf0.conn);
+ memif_delete (&intf1.conn);
+ memif_delete_socket (&memif_socket);
+ return ret;
+}
diff --git a/extras/libmemif/examples/test_app/main.c b/extras/libmemif/examples/test_app/main.c
new file mode 100644
index 00000000000..54c53921fe7
--- /dev/null
+++ b/extras/libmemif/examples/test_app/main.c
@@ -0,0 +1,324 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2022 Cisco Systems, Inc.
+ */
+#include <stdlib.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <libmemif.h>
+#include <common.h>
+
+#define APP_NAME "test_app"
+
+#define IF_NAME0 "libmemif0"
+#define IF_ID0 0
+#define IF_NAME1 "libmemif1"
+#define IF_ID1 1
+#define SOCKET_PATH "/run/vpp/memif.sock"
+
+memif_connection_t intf0, intf1;
+int epfd;
+
+/* informs user about connected status. private_ctx is used by user to identify
+ * connection */
+int
+on_connect (memif_conn_handle_t conn, void *private_ctx)
+{
+ INFO ("memif connected!");
+ int err;
+
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+
+ c->is_connected = 1;
+ alloc_memif_buffers (c);
+
+ err = memif_refill_queue (conn, 0, -1, 0);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ return err;
+ }
+
+ print_memif_details (c);
+
+ return 0;
+}
+
+/* informs user about disconnected status. private_ctx is used by user to
+ * identify connection */
+int
+on_disconnect (memif_conn_handle_t conn, void *private_ctx)
+{
+ INFO ("memif disconnected!");
+
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+
+ c->is_connected = 0;
+ free_memif_buffers (c);
+
+ /* stop event polling thread */
+ int err = memif_cancel_poll_event (memif_get_socket_handle (conn));
+ if (err != MEMIF_ERR_SUCCESS)
+ INFO ("We are doomed...");
+
+ return 0;
+}
+
+int
+on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
+{
+ memif_connection_t *c = (memif_connection_t *) private_ctx;
+ memif_connection_t *s, *r;
+ int err, i;
+ uint16_t tx;
+
+ if (c == &intf0)
+ {
+ r = &intf0;
+ s = &intf1;
+ }
+ else
+ {
+ r = &intf1;
+ s = &intf0;
+ }
+
+ /* receive packets from the shared memory */
+ err =
+ memif_rx_burst (r->conn, qid, r->rx_bufs, MAX_MEMIF_BUFS, &r->rx_buf_num);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_rx_burst: %s", memif_strerror (err));
+ return err;
+ }
+
+ do
+ {
+ /* allocate tx buffers */
+ err = memif_buffer_alloc (s->conn, s->tx_qid, s->tx_bufs, r->rx_buf_num,
+ &s->tx_buf_num, s->buffer_size);
+ /* suppress full ring error MEMIF_ERR_NOBUF_RING */
+ if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING)
+ {
+ INFO ("memif_buffer_alloc: %s", memif_strerror (err));
+ goto error;
+ }
+
+ /* Process the packets */
+ for (i = 0; i < s->tx_buf_num; i++)
+ {
+ memcpy (s->tx_bufs[i].data, r->rx_bufs[i].data, r->rx_bufs[i].len);
+ s->tx_bufs[i].flags = r->rx_bufs[i].flags;
+ s->tx_bufs[i].len = r->rx_bufs[i].len;
+ }
+
+ /* Done processing packets */
+ /* refill the queue */
+ err = memif_refill_queue (r->conn, qid, s->tx_buf_num, 0);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ goto error;
+ }
+ r->rx_buf_num -= s->tx_buf_num;
+
+ err =
+ memif_tx_burst (s->conn, s->tx_qid, s->tx_bufs, s->tx_buf_num, &tx);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_tx_burst: %s", memif_strerror (err));
+ goto error;
+ }
+ s->tx_buf_num -= tx;
+ /* This should never happen */
+ if (s->tx_buf_num != 0)
+ {
+ INFO ("memif_tx_burst failed to send all allocated buffers.");
+ goto error;
+ }
+ }
+ while (r->rx_buf_num > 0);
+
+ return 0;
+
+error:
+ err = memif_refill_queue (conn, qid, r->rx_buf_num, 0);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_refill_queue: %s", memif_strerror (err));
+ return err;
+ }
+ r->rx_buf_num = 0;
+
+ return -1;
+}
+
+void
+print_help ()
+{
+ printf ("LIBMEMIF TEST APP: %s", APP_NAME);
+#ifdef TEST_DBG
+ printf (" (debug)");
+#endif
+ printf ("\n");
+ printf ("==============================\n");
+ print_version ();
+ printf ("==============================\n");
+ printf (
+ "In this testing application, memif endpoints connect to an external "
+ "application.\n");
+ printf ("The test application loopbacks recieved packets from one memif to "
+ "another memif .\n");
+ printf ("The program will exit once the interfaces are disconnected.\n");
+ printf ("==============================\n");
+ printf ("Usage: test_app [OPTIONS]\n\n");
+ printf ("Options:\n");
+ printf ("\t-r\tInterface role <slave|master>. Default: slave\n");
+ printf ("\t-s\tSocket path. Supports abstract socket using @ before the "
+ "path. Default: /run/vpp/memif.sock\n");
+ printf ("\t-i\tInterface id. Default: 0\n");
+ printf ("\t-t\tInterface id2. Default: 1\n");
+ printf ("\t-b\tBuffer Size. Default: 2048\n");
+ printf ("\t-h\tShow help and exit.\n");
+ printf ("\t-v\tShow libmemif and memif version information and exit.\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+ memif_socket_args_t memif_socket_args = { 0 };
+ memif_socket_handle_t memif_socket;
+ memif_conn_args_t memif_conn_args = { 0 };
+ int opt, err, ret = 0;
+ uint8_t is_master = 0;
+ char socket_path[108];
+ int id0 = IF_ID0;
+ int id1 = IF_ID1;
+
+ strncpy (socket_path, SOCKET_PATH, sizeof (SOCKET_PATH));
+
+ /* prepare the private data */
+ memset (&intf0, 0, sizeof (intf0));
+ memset (&intf1, 0, sizeof (intf1));
+
+ while ((opt = getopt (argc, argv, "r:s:i:t:b:hv")) != -1)
+ {
+ switch (opt)
+ {
+ case 'r':
+ if (strncmp (optarg, "master", sizeof (optarg)) == 0)
+ {
+ is_master = 1;
+ }
+ else if (strncmp (optarg, "slave", sizeof (optarg)) == 0)
+ {
+ is_master = 0;
+ }
+ else
+ {
+ INFO ("Invalid role value: '%s'", optarg);
+ return -1;
+ }
+ break;
+ case 's':
+ sprintf (socket_path, "%s", optarg);
+ break;
+ case 'i':
+ id0 = atoi (optarg);
+ break;
+ case 't':
+ id1 = atoi (optarg);
+ break;
+ case 'b':
+ intf1.buffer_size = intf0.buffer_size = atoi (optarg);
+ break;
+ case 'h':
+ print_help ();
+ return 0;
+ case 'v':
+ print_version ();
+ return 0;
+ }
+ }
+
+ /** Create memif socket
+ *
+ * Interfaces are internally stored in a database referenced by memif socket.
+ */
+ sprintf (memif_socket_args.path, "%s", socket_path);
+ /* Set application name */
+ strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME));
+
+ /* configure autoconnect timer */
+ if (is_master == 0)
+ {
+ memif_socket_args.connection_request_timer.it_value.tv_sec = 2;
+ memif_socket_args.connection_request_timer.it_value.tv_nsec = 0;
+ memif_socket_args.connection_request_timer.it_interval.tv_sec = 2;
+ memif_socket_args.connection_request_timer.it_interval.tv_nsec = 0;
+ }
+
+ err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_create_socket: %s", memif_strerror (err));
+ goto error;
+ }
+
+ /**
+ * Create memif interfaces
+ */
+ memif_conn_args.socket = memif_socket;
+ memif_conn_args.interface_id = id0;
+ strncpy (memif_conn_args.interface_name, IF_NAME0,
+ sizeof (memif_conn_args.interface_name));
+ memif_conn_args.is_master = is_master;
+ if (intf0.buffer_size)
+ memif_conn_args.buffer_size = intf0.buffer_size;
+ else
+ memif_conn_args.buffer_size = intf0.buffer_size = intf1.buffer_size = 2048;
+
+ err = memif_create (&intf0.conn, &memif_conn_args, on_connect, on_disconnect,
+ on_interrupt, (void *) &intf0);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_create_socket: %s", memif_strerror (err));
+ return err;
+ }
+
+ memif_conn_args.interface_id = id1;
+ strncpy (memif_conn_args.interface_name, IF_NAME1,
+ sizeof (memif_conn_args.interface_name));
+
+ err = memif_create (&intf1.conn, &memif_conn_args, on_connect, on_disconnect,
+ on_interrupt, (void *) &intf1);
+ if (err != MEMIF_ERR_SUCCESS)
+ {
+ INFO ("memif_create_socket: %s", memif_strerror (err));
+ return err;
+ }
+
+ do
+ {
+ err = memif_poll_event (memif_socket, -1);
+ }
+ while (err == MEMIF_ERR_SUCCESS);
+
+ return 0;
+
+error:
+ ret = -1;
+done:
+ free_memif_buffers (&intf0);
+ free_memif_buffers (&intf1);
+ memif_delete (&intf0.conn);
+ memif_delete (&intf1.conn);
+ memif_delete_socket (&memif_socket);
+ return ret;
+}
diff --git a/extras/libmemif/libmemif_doc.md b/extras/libmemif/libmemif_doc.md
deleted file mode 100644
index 1260d9c60ac..00000000000
--- a/extras/libmemif/libmemif_doc.md
+++ /dev/null
@@ -1,77 +0,0 @@
-Shared Memory Packet Interface (memif) Library {#libmemif_doc}
-==============================================
-
-## Introduction
-
-Shared memory packet interface (memif) provides high performance packet transmit and receive between user application and Vector Packet Processing (VPP) or multiple user applications. Using libmemif, user application can create shared memory interface in master or slave mode and connect to VPP or another application using libmemif. Once the connection is established, user application can receive or transmit packets using libmemif API.
-
-![Architecture](docs/architecture.png)
-
-## Features
-
-- [x] Slave mode
- - [x] Connect to VPP over memif
- - [x] ICMP responder example app
-- [x] Transmit/receive packets
-- [x] Interrupt mode support
-- [x] File descriptor event polling in libmemif (optional)
- - [x] Simplify file descriptor event polling (one handler for control and interrupt channel)
-- [x] Multiple connections
-- [x] Multiple queues
- - [x] Multi-thread support
-- [x] Master mode
- - [ ] Multiple regions (TODO)
-- [ ] Performance testing (TODO)
-
-## Quickstart
-
-This setup will run libmemif ICMP responder example app in container. Install [docker](https://docs.docker.com/engine/installation) engine.
-Useful link: [Docker documentation](https://docs.docker.com/get-started).
-
-Pull image:
-```
-# docker pull ligato/libmemif-sample-service
-```
-
-Now you should be able to see ligato/libmemif-sample-service image on your local machine (IMAGE ID in this README may be outdated):
-```
-# docker images
-REPOSITORY TAG IMAGE ID CREATED SIZE
-ligato/libmemif-sample-service latest 32ecc2f9d013 About a minute ago 468MB
-...
-```
-
-Run container:
-```
-# docker run -it --rm --name icmp-responder --hostname icmp-responder --privileged -v "/run/vpp/:/run/vpp/" ligato/libmemif-sample-service
-```
-Example application will start in debug mode. Output should look like this:
-```
-ICMP_Responder:add_epoll_fd:233: fd 0 added to epoll
-ICMP_Responder:add_epoll_fd:233: fd 5 added to epoll
-LIBMEMIF EXAMPLE APP: ICMP_Responder (debug)
-==============================
-libmemif version: 2.0 (debug)
-memif version: 512
-commands:
- help - prints this help
- exit - exit app
- conn <index> <mode> [<interrupt-desc>] - create memif. index is also used as interface id, mode 0 = slave 1 = master, interrupt-desc none = default 0 = if ring is full wait 1 = handle only ARP requests
- del <index> - delete memif
- show - show connection details
- ip-set <index> <ip-addr> - set interface ip address
- rx-mode <index> <qid> <polling|interrupt> - set queue rx mode
- sh-count - print counters
- cl-count - clear counters
- send <index> <tx> <ip> <mac> - send icmp
-```
-
-Continue with @ref libmemif_example_setup which contains instructions on how to set up connection between icmpr-epoll example app and VPP-memif.
-
-#### Next steps
-
-- @subpage libmemif_build_doc
-- @subpage libmemif_examples_doc
-- @subpage libmemif_example_setup_doc
-- @subpage libmemif_gettingstarted_doc
-- @subpage libmemif_devperftest_doc
diff --git a/extras/libmemif/libmemif_doc.rst b/extras/libmemif/libmemif_doc.rst
new file mode 100644
index 00000000000..281c5487c90
--- /dev/null
+++ b/extras/libmemif/libmemif_doc.rst
@@ -0,0 +1,66 @@
+.. _libmemif_doc:
+
+Shared Memory Packet Interface (memif) Library
+==============================================
+
+Features
+--------
+
+- ✅ Slave mode
+
+ - ✅ Connect to VPP over memif
+ - ✅ ICMP responder example app
+
+- ✅ Transmit/receive packets
+- ✅ Interrupt mode support
+- ✅ File descriptor event polling in libmemif (optional)
+
+ - ✅ Simplify file descriptor event polling (one handler for control
+ and interrupt channel)
+
+- ✅ Multiple connections
+- ✅ Multiple queues
+
+ - ✅ Multi-thread support
+
+- ✅ Master mode
+
+ - ✅ Multiple regions
+
+- ✅ Loopback
+
+Quickstart
+----------
+
+This setup will run libmemif ICMP responder example app in container.
+Install `docker <https://docs.docker.com/engine/installation>`__ engine.
+Useful link: `Docker
+documentation <https://docs.docker.com/get-started>`__.
+
+Build the docker image:
+
+::
+
+ # docker build . -t libmemif
+
+Now you should be able to see libmemif image on your local machine:
+
+::
+
+ # docker images
+ REPOSITORY TAG IMAGE ID CREATED SIZE
+ libmemif latest 32ecc2f9d013 About a minute ago 468MB
+ ...
+
+Run container:
+
+::
+
+ # docker run -it --rm --name icmp-responder --hostname icmp-responder --privileged -v "/run/vpp/:/run/vpp/" libmemif
+
+The interface will by default connect to a master interface listening on
+``/run/vpp/master.sock``. The example will handle ARP requests and
+respond to ICMPv4 requests to ``192.168.1.1``.
+
+Continue with :ref:`libmemif_examples_doc` which contains instructions on
+how to set up connection between icmp_responder example app and VPP-memif.
diff --git a/extras/libmemif/src/CMakeLists.txt b/extras/libmemif/src/CMakeLists.txt
index ddb8a52f82b..8b3223d0990 100644
--- a/extras/libmemif/src/CMakeLists.txt
+++ b/extras/libmemif/src/CMakeLists.txt
@@ -34,6 +34,7 @@ include_directories(${HEADERS_DIR})
add_library(memif SHARED ${MEMIF_SOURCES})
target_link_libraries(memif ${CMAKE_THREAD_LIBS_INIT})
+target_compile_definitions(memif PUBLIC MEMIF_CACHELINE_SIZE=${LIBMEMIF_CACHELINE_SIZE})
find_library(LIB_BSD bsd)
if(LIB_BSD)
diff --git a/extras/libmemif/src/libmemif.h b/extras/libmemif/src/libmemif.h
index 12d2f1e3af4..e604c40b20e 100644
--- a/extras/libmemif/src/libmemif.h
+++ b/extras/libmemif/src/libmemif.h
@@ -23,7 +23,7 @@
#define _LIBMEMIF_H_
/** Libmemif version. */
-#define LIBMEMIF_VERSION "3.1"
+#define LIBMEMIF_VERSION "4.0"
/** Default name of application using libmemif. */
#define MEMIF_DEFAULT_APP_NAME "libmemif-app"
@@ -35,50 +35,51 @@
/*! Error codes */
typedef enum
{
- MEMIF_ERR_SUCCESS = 0, /*!< success */
-/* SYSCALL ERRORS */
- MEMIF_ERR_SYSCALL, /*!< other syscall error */
- MEMIF_ERR_CONNREFUSED, /*!< connection refused */
- MEMIF_ERR_ACCES, /*!< permission denied */
- MEMIF_ERR_NO_FILE, /*!< file does not exist */
- MEMIF_ERR_FILE_LIMIT, /*!< system open file limit */
- MEMIF_ERR_PROC_FILE_LIMIT, /*!< process open file limit */
- MEMIF_ERR_ALREADY, /*!< connection already requested */
- MEMIF_ERR_AGAIN, /*!< fd is not socket, or operation would block */
- MEMIF_ERR_BAD_FD, /*!< invalid fd */
- MEMIF_ERR_NOMEM, /*!< out of memory */
-/* LIBMEMIF ERRORS */
- MEMIF_ERR_INVAL_ARG, /*!< invalid argument */
- MEMIF_ERR_NOCONN, /*!< handle points to no connection */
- MEMIF_ERR_CONN, /*!< handle points to existing connection */
- MEMIF_ERR_CB_FDUPDATE, /*!< user defined callback memif_control_fd_update_t error */
- MEMIF_ERR_FILE_NOT_SOCK, /*!< file specified by socket filename
- exists, but it's not socket */
- MEMIF_ERR_NO_SHMFD, /*!< missing shm fd */
- MEMIF_ERR_COOKIE, /*!< wrong cookie on ring */
- MEMIF_ERR_NOBUF_RING, /*!< ring buffer full */
- MEMIF_ERR_NOBUF, /*!< not enough memif buffers */
- MEMIF_ERR_NOBUF_DET, /*!< memif details needs larger buffer */
- MEMIF_ERR_INT_WRITE, /*!< send interrupt error */
- MEMIF_ERR_MFMSG, /*!< malformed msg received */
- MEMIF_ERR_QID, /*!< invalid queue id */
-/* MEMIF PROTO ERRORS */
- MEMIF_ERR_PROTO, /*!< incompatible protocol version */
- MEMIF_ERR_ID, /*!< unmatched interface id */
- MEMIF_ERR_ACCSLAVE, /*!< slave cannot accept connection requests */
- MEMIF_ERR_ALRCONN, /*!< memif is already connected */
- MEMIF_ERR_MODE, /*!< mode mismatch */
- MEMIF_ERR_SECRET, /*!< secret mismatch */
- MEMIF_ERR_NOSECRET, /*!< secret required */
- MEMIF_ERR_MAXREG, /*!< max region limit reached */
- MEMIF_ERR_MAXRING, /*!< max ring limit reached */
- MEMIF_ERR_NO_INTFD, /*!< missing interrupt fd */
- MEMIF_ERR_DISCONNECT, /*!< disconnect received */
- MEMIF_ERR_DISCONNECTED, /*!< peer interface disconnected */
- MEMIF_ERR_UNKNOWN_MSG, /*!< unknown message type */
- MEMIF_ERR_POLL_CANCEL, /*!< memif_poll_event() was cancelled */
- MEMIF_ERR_MAX_RING, /*!< too large ring size */
- MEMIF_ERR_PRIVHDR, /*!< private hdrs not supported */
+ MEMIF_ERR_SUCCESS = 0, /*!< success */
+ /* SYSCALL ERRORS */
+ MEMIF_ERR_SYSCALL, /*!< other syscall error */
+ MEMIF_ERR_CONNREFUSED, /*!< connection refused */
+ MEMIF_ERR_ACCES, /*!< permission denied */
+ MEMIF_ERR_NO_FILE, /*!< file does not exist */
+ MEMIF_ERR_FILE_LIMIT, /*!< system open file limit */
+ MEMIF_ERR_PROC_FILE_LIMIT, /*!< process open file limit */
+ MEMIF_ERR_ALREADY, /*!< connection already requested */
+ MEMIF_ERR_AGAIN, /*!< fd is not socket, or operation would block */
+ MEMIF_ERR_BAD_FD, /*!< invalid fd */
+ MEMIF_ERR_NOMEM, /*!< out of memory */
+ /* LIBMEMIF ERRORS */
+ MEMIF_ERR_INVAL_ARG, /*!< invalid argument */
+ MEMIF_ERR_NOCONN, /*!< handle points to no connection */
+ MEMIF_ERR_CONN, /*!< handle points to existing connection */
+ MEMIF_ERR_CB_FDUPDATE, /*!< user defined callback memif_control_fd_update_t
+ error */
+ MEMIF_ERR_FILE_NOT_SOCK, /*!< file specified by socket path
+ exists, but it's not socket */
+ MEMIF_ERR_NO_SHMFD, /*!< missing shm fd */
+ MEMIF_ERR_COOKIE, /*!< wrong cookie on ring */
+ MEMIF_ERR_NOBUF_RING, /*!< ring buffer full */
+ MEMIF_ERR_NOBUF, /*!< not enough memif buffers */
+ MEMIF_ERR_NOBUF_DET, /*!< memif details needs larger buffer */
+ MEMIF_ERR_INT_WRITE, /*!< send interrupt error */
+ MEMIF_ERR_MFMSG, /*!< malformed msg received */
+ MEMIF_ERR_QID, /*!< invalid queue id */
+ /* MEMIF PROTO ERRORS */
+ MEMIF_ERR_PROTO, /*!< incompatible protocol version */
+ MEMIF_ERR_ID, /*!< unmatched interface id */
+ MEMIF_ERR_ACCSLAVE, /*!< slave cannot accept connection requests */
+ MEMIF_ERR_ALRCONN, /*!< memif is already connected */
+ MEMIF_ERR_MODE, /*!< mode mismatch */
+ MEMIF_ERR_SECRET, /*!< secret mismatch */
+ MEMIF_ERR_NOSECRET, /*!< secret required */
+ MEMIF_ERR_MAXREG, /*!< max region limit reached */
+ MEMIF_ERR_MAXRING, /*!< max ring limit reached */
+ MEMIF_ERR_NO_INTFD, /*!< missing interrupt fd */
+ MEMIF_ERR_DISCONNECT, /*!< disconnect received */
+ MEMIF_ERR_DISCONNECTED, /*!< peer interface disconnected */
+ MEMIF_ERR_UNKNOWN_MSG, /*!< unknown message type */
+ MEMIF_ERR_POLL_CANCEL, /*!< memif_poll_event() was cancelled */
+ MEMIF_ERR_MAX_RING, /*!< too large ring size */
+ MEMIF_ERR_PRIVHDR, /*!< private hdrs not supported */
} memif_err_t;
/**
@@ -87,23 +88,24 @@ typedef enum
* @{
*/
-/** user needs to set events that occurred on fd and pass them to memif_control_fd_handler */
-#define MEMIF_FD_EVENT_READ (1 << 0)
-#define MEMIF_FD_EVENT_WRITE (1 << 1)
-/** inform libmemif that error occurred on fd */
-#define MEMIF_FD_EVENT_ERROR (1 << 2)
-/** if set, informs that fd is going to be closed (user may want to stop watching for events on this fd) */
-#define MEMIF_FD_EVENT_DEL (1 << 3)
-/** update events */
-#define MEMIF_FD_EVENT_MOD (1 << 4)
+/** \brief Memif fd events
+ * User needs to set events that occurred on fd and pass them to
+ * memif_control_fd_handler
+ */
+typedef enum memif_fd_event_type
+{
+ MEMIF_FD_EVENT_READ = 1, /* 00001 */
+ MEMIF_FD_EVENT_WRITE = 2, /* 00010 */
+ /** inform libmemif that error occurred on fd */
+ MEMIF_FD_EVENT_ERROR = 4, /* 00100 */
+ /** if set, informs that fd is going to be closed (user may want to stop
+ watching for events on this fd) */
+ MEMIF_FD_EVENT_DEL = 8, /* 01000 */
+ /** update events */
+ MEMIF_FD_EVENT_MOD = 16 /* 10000 */
+} memif_fd_event_type_t;
/** @} */
-/** \brief Memif per thread main handle
- Pointer of type void, pointing to internal structure.
- Used to identify internal per thread database.
-*/
-typedef void *memif_per_thread_main_handle_t;
-
/** \brief Memif connection handle
pointer of type void, pointing to internal structure
*/
@@ -144,19 +146,29 @@ typedef void (memif_free_t) (void *ptr);
* @{
*/
+/** \brief Memif fd event
+ @param fd - interrupt file descriptor
+ @param type - memif fd event type
+ @param private_ctx - private event data
+*/
+typedef struct memif_fd_event
+{
+ int fd;
+ memif_fd_event_type_t type;
+ void *private_ctx;
+} memif_fd_event_t;
+
/** \brief Memif control file descriptor update (callback function)
- @param fd - new file descriptor to watch
- @param events - event type(s) to watch for
- @param private_ctx - libmemif main private context. Is NULL for
- libmemif main created by memif_init()
+ @param fde - memif fd event
+ @param private_ctx - private context of socket this fd belongs to
This callback is called when there is new fd to watch for events on
- or if fd is about to be closed (user mey want to stop watching for events on this fd).
- Private context is taken from libmemif_main, 'private_ctx' passed to memif_per_thread_init()
- or NULL in case of memif_init()
+ or if fd is about to be closed (user mey want to stop watching for events
+ on this fd). Private context is taken from libmemif_main, 'private_ctx'
+ passed to memif_per_thread_init() or NULL in case of memif_init()
*/
-typedef int (memif_control_fd_update_t) (int fd, uint8_t events,
+typedef int (memif_control_fd_update_t) (memif_fd_event_t fde,
void *private_ctx);
/** \brief Memif connection status update (callback function)
@@ -176,8 +188,8 @@ typedef int (memif_connection_update_t) (memif_conn_handle_t conn,
Called when event is received on interrupt fd.
*/
-typedef int (memif_interrupt_t) (memif_conn_handle_t conn, void *private_ctx,
- uint16_t qid);
+typedef int (memif_on_interrupt_t) (memif_conn_handle_t conn,
+ void *private_ctx, uint16_t qid);
/** @} */
@@ -235,10 +247,11 @@ typedef int (memif_del_external_region_t) (void *addr, uint32_t size, int fd,
@param dr - delete external region callback
@param go - get external buffer offset callback (optional)
*/
-void memif_register_external_region (memif_add_external_region_t * ar,
- memif_get_external_region_addr_t * gr,
- memif_del_external_region_t * dr,
- memif_get_external_buffer_offset_t * go);
+void memif_register_external_region (memif_socket_handle_t sock,
+ memif_add_external_region_t *ar,
+ memif_get_external_region_addr_t *gr,
+ memif_del_external_region_t *dr,
+ memif_get_external_buffer_offset_t *go);
/** \brief Register external region
@param pt_main - per thread main handle
@@ -276,6 +289,37 @@ typedef enum
} memif_interface_mode_t;
#endif /* _MEMIF_H_ */
+/** \brief Memif socket arguments
+ @param path - UNIX socket path, supports abstract socket (have '\0' or '@'
+ as the first char of the path)
+ @param app_name - application name
+ @param connection_request_timer - automaticaly request connection each time
+ this timer expires, must be non-zero to enable this feature
+ @param on_control_fd_update - if control fd updates inform user to watch
+ new fd
+ @param alloc - custom memory allocator, NULL = default
+ @param realloc - custom memory reallocation, NULL = default
+ @param free - custom memory free, NULL = default
+
+ If param on_control_fd_update is set to NULL,
+ libmemif will handle file descriptor event polling
+ if a valid callback is set, file descriptor event polling needs to be done
+ by user application, all file descriptors and event types will be passed in
+ this callback to user application
+*/
+typedef struct memif_socket_args
+{
+ char path[108];
+ char app_name[32];
+
+ struct itimerspec connection_request_timer;
+
+ memif_control_fd_update_t *on_control_fd_update;
+ memif_alloc_t *alloc;
+ memif_realloc_t *realloc;
+ memif_free_t *free;
+} memif_socket_args_t;
+
/** \brief Memif connection arguments
@param socket - Memif socket handle, if NULL default socket will be used.
Default socket is only supported in global database (see memif_init).
@@ -353,7 +397,7 @@ typedef struct
typedef struct
{
uint8_t region;
- uint8_t qid;
+ uint16_t qid;
uint32_t ring_size;
/** if set queue is in polling mode, else in interrupt mode */
#define MEMIF_QUEUE_FLAG_POLLING 1
@@ -388,7 +432,7 @@ typedef struct
@param secret - secret
@param role - 0 = master, 1 = slave
@param mode - 0 = ethernet, 1 = ip , 2 = punt/inject
- @param socket_filename - socket filename
+ @param socket_path - socket path
@param regions_num - number of regions
@param regions - struct containing region details
@param rx_queues_num - number of receive queues
@@ -409,7 +453,7 @@ typedef struct
uint8_t *secret; /* optional */
uint8_t role; /* 0 = master, 1 = slave */
uint8_t mode; /* 0 = ethernet, 1 = ip, 2 = punt/inject */
- uint8_t *socket_filename;
+ uint8_t *socket_path;
uint8_t regions_num;
memif_region_details_t *regions;
uint8_t rx_queues_num;
@@ -433,7 +477,12 @@ typedef struct
\return ((MEMIF_VERSION_MAJOR << 8) | MEMIF_VERSION_MINOR)
*/
-uint16_t memif_get_version ();
+uint16_t memif_get_version (void);
+
+/** \brief Get memif version as string
+ \return major.minor
+*/
+const char *memif_get_version_str (void);
/** \brief Memif get queue event file descriptor
@param conn - memif connection handle
@@ -475,68 +524,6 @@ char *memif_strerror (int err_code);
int memif_get_details (memif_conn_handle_t conn, memif_details_t * md,
char *buf, ssize_t buflen);
-/** \brief Memif initialization
- @param on_control_fd_update - if control fd updates inform user to watch new fd
- @param app_name - application name (will be truncated to 32 chars)
- @param memif_alloc - custom memory allocator, NULL = default
- @param memif_realloc - custom memory reallocation, NULL = default
- @param memif_free - custom memory free, NULL = default
-
- if param on_control_fd_update is set to NULL,
- libmemif will handle file descriptor event polling
- if a valid callback is set, file descriptor event polling needs to be done by
- user application, all file descriptors and event types will be passed in
- this callback to user application
-
- Initialize internal libmemif structures. Create timerfd (used to periodically request connection by
- disconnected memifs in slave mode, with no additional API call). This fd is passed to user with memif_control_fd_update_t
- timer is inactive at this state. It activates with if there is at least one memif in slave mode.
-
- \return memif_err_t
-*/
-int memif_init (memif_control_fd_update_t * on_control_fd_update,
- char *app_name, memif_alloc_t * memif_alloc,
- memif_realloc_t * memif_realloc, memif_free_t * memif_free);
-
-/** \brief Memif per thread initialization
- @param pt_main - per thread main handle
- @param private_ctx - private context
- @param on_control_fd_update - if control fd updates inform user to watch new fd
- @param app_name - application name (will be truncated to 32 chars)
- @param memif_alloc - custom memory allocator, NULL = default
- @param memif_realloc - custom memory reallocation, NULL = default
- @param memif_free - custom memory free, NULL = default
-
- Per thread version of memif_init ().
- Instead of using global database, creates and initializes unique database,
- identified by 'memif_per_thread_main_handle_t'.
-
- \return memif_err_t
-*/
-int memif_per_thread_init (memif_per_thread_main_handle_t * pt_main,
- void *private_ctx,
- memif_control_fd_update_t * on_control_fd_update,
- char *app_name, memif_alloc_t * memif_alloc,
- memif_realloc_t * memif_realloc,
- memif_free_t * memif_free);
-
-/** \brief Memif cleanup
-
- Free libmemif internal allocations.
-
- \return 0
-*/
-int memif_cleanup ();
-
-/** \brief Memif per thread cleanup
- @param pt_main - per thread main handle
-
- Free libmemif internal allocations and sets the handle to NULL.
-
- \return memif_err_t
-*/
-int memif_per_thread_cleanup (memif_per_thread_main_handle_t * pt_main);
-
/** \brief Memory interface create function
@param conn - connection handle for client app
@param args - memory interface connection arguments
@@ -560,43 +547,19 @@ int memif_per_thread_cleanup (memif_per_thread_main_handle_t * pt_main);
\return memif_err_t
*/
-int memif_create (memif_conn_handle_t * conn, memif_conn_args_t * args,
- memif_connection_update_t * on_connect,
- memif_connection_update_t * on_disconnect,
- memif_interrupt_t * on_interrupt, void *private_ctx);
+int memif_create (memif_conn_handle_t *conn, memif_conn_args_t *args,
+ memif_connection_update_t *on_connect,
+ memif_connection_update_t *on_disconnect,
+ memif_on_interrupt_t *on_interrupt, void *private_ctx);
/** \brief Memif control file descriptor handler
- @param fd - file descriptor on which the event occurred
+ @param ptr - pointer to event data
@param events - event type(s) that occurred
- If event occurs on any control fd, call memif_control_fd_handler.
- Internal - lib will "identify" fd (timerfd, listener, control) and handle event accordingly.
-
- FD-TYPE -
- TIMERFD -
- Every disconnected memif in slave mode will request connection.
- LISTENER or CONTROL -
- Handle socket messaging (internal connection establishment).
- INTERRUPT -
- Call on_interrupt callback (if set).
-
- \return memif_err_t
-
-*/
-int memif_control_fd_handler (int fd, uint8_t events);
-
-/** \brief Memif per thread control file descriptor handler
- @param pt_main - per thread main handle
- @param fd - file descriptor on which the event occurred
- @param events - event type(s) that occurred
-
- Per thread version of memif_control_fd_handler.
-
\return memif_err_t
*/
-int memif_per_thread_control_fd_handler (memif_per_thread_main_handle_t
- pt_main, int fd, uint8_t events);
+int memif_control_fd_handler (void *ptr, memif_fd_event_type_t events);
/** \brief Memif delete
@param conn - pointer to memif connection handle
@@ -700,28 +663,19 @@ int memif_rx_burst (memif_conn_handle_t conn, uint16_t qid,
memif_buffer_t * bufs, uint16_t count, uint16_t * rx);
/** \brief Memif poll event
+ @param sock - socket to poll events on
@param timeout - timeout in seconds
Passive event polling -
- timeout = 0 - dont wait for event, check event queue if there is an event and return.
- timeout = -1 - wait until event
+ timeout = 0 - dont wait for event, check event queue if there is an event
+ and return. timeout = -1 - wait until event
\return memif_err_t
*/
-int memif_poll_event (int timeout);
-
-/** \brief Memif per thread poll event
- @param pt_main - per thread main handle
- @param timeout - timeout in seconds
-
- Per thread version of memif_poll_event.
-
- \return memif_err_t
-*/
-int memif_per_thread_poll_event (memif_per_thread_main_handle_t pt_main,
- int timeout);
+int memif_poll_event (memif_socket_handle_t sock, int timeout);
/** \brief Send signal to stop concurrently running memif_poll_event().
+ @param sock - stop polling on this socket
The function, however, does not wait for memif_poll_event() to stop.
memif_poll_event() may still return simply because an event has occurred
@@ -734,39 +688,7 @@ int memif_per_thread_poll_event (memif_per_thread_main_handle_t pt_main,
\return memif_err_t
*/
#define MEMIF_HAVE_CANCEL_POLL_EVENT 1
-int memif_cancel_poll_event ();
-/** \brief Send signal to stop concurrently running memif_poll_event().
- @param pt_main - per thread main handle
-
- Per thread version of memif_cancel_poll_event.
-
- \return memif_err_t
-*/
-int memif_per_thread_cancel_poll_event (memif_per_thread_main_handle_t
- pt_main);
-
-/** \brief Set connection request timer value
- @param timer - new timer value
-
- Timer on which all disconnected slaves request connection.
- See system call 'timer_settime' man-page.
-
- \return memif_err_t
-*/
-int memif_set_connection_request_timer (struct itimerspec timer);
-
-/** \brief Set connection request timer value
- @param pt_main - per thread main handle
- @param timer - new timer value
-
- Per thread version of memif_set_connection_request_timer
-
- \return memif_err_t
-*/
-int
-memif_per_thread_set_connection_request_timer (memif_per_thread_main_handle_t
- pt_main,
- struct itimerspec timer);
+int memif_cancel_poll_event (memif_socket_handle_t sock);
/** \brief Send connection request
@param conn - memif connection handle
@@ -779,34 +701,27 @@ int memif_request_connection (memif_conn_handle_t conn);
/** \brief Create memif socket
@param sock - socket handle for client app
- @param filename - path to socket file
+ @param args - memif socket arguments
@param private_ctx - private context
The first time an interface is assigned a socket, its type is determined.
- For master role it's 'listener', for slave role it's 'client'. Each interface
- requires socket of its respective type. Default socket is created if no
- socket handle is passed to memif_create(). It's private context is NULL.
- If all interfaces using this socket are deleted, the socket returns
- to its default state.
+ For master role it's 'listener', for slave role it's 'client'. Each
+ interface requires socket of its respective type. Default socket is created
+ if no socket handle is passed to memif_create(). It's private context is
+ NULL. If all interfaces using this socket are deleted, the socket returns to
+ its default state.
\return memif_err_t
*/
-int memif_create_socket (memif_socket_handle_t * sock, const char *filename,
- void *private_ctx);
+int memif_create_socket (memif_socket_handle_t *sock,
+ memif_socket_args_t *args, void *private_ctx);
-/** \brief Create memif socket
- @param pt_main - per thread main handle
- @param sock - socket handle for client app
- @param filename - path to socket file
- @param private_ctx - private context
-
- Per thread version of memif_create_socket.
+/** \brief Get memif socket handle from connection
+ @param conn - memif connection handle
- \return memif_err_t
+ \return memif_socket_handle_t
*/
-int memif_per_thread_create_socket (memif_per_thread_main_handle_t pt_main,
- memif_socket_handle_t * sock,
- const char *filename, void *private_ctx);
+memif_socket_handle_t memif_get_socket_handle (memif_conn_handle_t conn);
/** \brief Delete memif socket
@param sock - socket handle for client app
@@ -818,14 +733,41 @@ int memif_per_thread_create_socket (memif_per_thread_main_handle_t pt_main,
*/
int memif_delete_socket (memif_socket_handle_t * sock);
-/** \brief Get socket filename
+/** \brief Get socket path
@param sock - socket handle for client app
- Return constant pointer to socket filename.
+ Return constant pointer to socket path.
\return const char *
*/
-const char *memif_get_socket_filename (memif_socket_handle_t sock);
+const char *memif_get_socket_path (memif_socket_handle_t sock);
+
+/** \brief Get listener file descriptor
+ @param sock - memif socket handle
+
+ \return listener fd
+*/
+int memif_get_listener_fd (memif_socket_handle_t sock);
+
+/** \brief Set listener file descriptor
+ @param sock - memif socket handle
+ @param if - file descriptor
+
+ \return memif_err_t
+*/
+int memif_set_listener_fd (memif_socket_handle_t sock, int fd);
+
+/** \brief Set connection request timer value
+ @param sock - memif socket handle
+ @param timer - new timer value
+
+ Timer on which all disconnected slaves request connection.
+ If the timer doesn't exist (timerspec is 0) create new timer.
+ See system call 'timer_settime' man-page.
+ \return memif_err_t
+*/
+int memif_set_connection_request_timer (memif_socket_handle_t sock,
+ struct itimerspec timer);
/** @} */
diff --git a/extras/libmemif/src/main.c b/extras/libmemif/src/main.c
index e70334ae40f..6f9ba2fe229 100644
--- a/extras/libmemif/src/main.c
+++ b/extras/libmemif/src/main.c
@@ -65,8 +65,6 @@
#define MEMIF_MEMORY_BARRIER() __sync_synchronize ()
#endif /* __x86_x64__ */
-libmemif_main_t libmemif_main;
-
static char memif_buf[MAX_ERRBUF_LEN];
const char *memif_errlist[ERRLIST_LEN] = { /* MEMIF_ERR_SUCCESS */
@@ -163,6 +161,7 @@ memif_strerror (int err_code)
else
{
strlcpy (memif_buf, memif_errlist[err_code], sizeof (memif_buf));
+ memif_buf[strlen (memif_errlist[err_code])] = '\0';
}
return memif_buf;
}
@@ -173,6 +172,16 @@ memif_get_version ()
return MEMIF_VERSION;
}
+const char *
+memif_get_version_str ()
+{
+#define __STR_HELPER(x) #x
+#define __STR(x) __STR_HELPER (x)
+ return __STR (MEMIF_VERSION_MAJOR) "." __STR (MEMIF_VERSION_MINOR);
+#undef __STR
+#undef __STR_HELPER
+}
+
#define DBG_TX_BUF (0)
#define DBG_RX_BUF (1)
@@ -231,587 +240,128 @@ memif_syscall_error_handler (int err_code)
return MEMIF_ERR_SYSCALL;
}
-/* Always valid */
-libmemif_main_t *
-get_libmemif_main (memif_socket_t * ms)
-{
- if (ms != NULL && ms->lm != NULL)
- return ms->lm;
- return &libmemif_main;
-}
-
static int
-memif_add_epoll_fd (libmemif_main_t * lm, int fd, uint32_t events)
+memif_add_epoll_fd (memif_socket_t *ms, memif_fd_event_t fde, uint32_t events)
{
- if (fd < 0)
+ if (fde.fd < 0)
{
- DBG ("invalid fd %d", fd);
+ DBG ("invalid fd %d", fde.fd);
return -1;
}
struct epoll_event evt;
memset (&evt, 0, sizeof (evt));
evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (lm->epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
+ evt.data.ptr = fde.private_ctx;
+ if (epoll_ctl (ms->epfd, EPOLL_CTL_ADD, fde.fd, &evt) < 0)
{
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
+ DBG ("epoll_ctl: %s fd %d", strerror (errno), fde.fd);
return -1;
}
- DBG ("fd %d added to epoll", fd);
+ DBG ("fd %d added to epoll", fde.fd);
return 0;
}
static int
-memif_mod_epoll_fd (libmemif_main_t * lm, int fd, uint32_t events)
+memif_mod_epoll_fd (memif_socket_t *ms, memif_fd_event_t fde, uint32_t events)
{
- if (fd < 0)
+ if (fde.fd < 0)
{
- DBG ("invalid fd %d", fd);
+ DBG ("invalid fd %d", fde.fd);
return -1;
}
struct epoll_event evt;
memset (&evt, 0, sizeof (evt));
evt.events = events;
- evt.data.fd = fd;
- if (epoll_ctl (lm->epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
+ evt.data.ptr = fde.private_ctx;
+ if (epoll_ctl (ms->epfd, EPOLL_CTL_MOD, fde.fd, &evt) < 0)
{
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
+ DBG ("epoll_ctl: %s fd %d", strerror (errno), fde.fd);
return -1;
}
- DBG ("fd %d modified on epoll", fd);
+ DBG ("fd %d modified on epoll", fde.fd);
return 0;
}
static int
-memif_del_epoll_fd (libmemif_main_t * lm, int fd)
+memif_del_epoll_fd (memif_socket_t *ms, memif_fd_event_t fde)
{
- if (fd < 0)
+ if (fde.fd < 0)
{
- DBG ("invalid fd %d", fd);
+ DBG ("invalid fd %d", fde.fd);
return -1;
}
struct epoll_event evt;
memset (&evt, 0, sizeof (evt));
- if (epoll_ctl (lm->epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
+ if (epoll_ctl (ms->epfd, EPOLL_CTL_DEL, fde.fd, &evt) < 0)
{
- DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
+ DBG ("epoll_ctl: %s fd %d", strerror (errno), fde.fd);
return -1;
}
- DBG ("fd %d removed from epoll", fd);
+ DBG ("fd %d removed from epoll", fde.fd);
return 0;
}
int
-memif_control_fd_update (int fd, uint8_t events, void *private_ctx)
+memif_control_fd_update (memif_fd_event_t fde, void *private_ctx)
{
- libmemif_main_t *lm;
+ memif_socket_t *ms = (memif_socket_t *) private_ctx;
+ int fd;
- lm = (private_ctx == NULL) ? &libmemif_main : (libmemif_main_t *) private_ctx;
+ if (ms == NULL)
+ return MEMIF_ERR_INVAL_ARG;
- if (events & MEMIF_FD_EVENT_DEL)
- return memif_del_epoll_fd (lm, fd);
+ if (fde.type & MEMIF_FD_EVENT_DEL)
+ return memif_del_epoll_fd (ms, fde);
uint32_t evt = 0;
- if (events & MEMIF_FD_EVENT_READ)
+ if (fde.type & MEMIF_FD_EVENT_READ)
evt |= EPOLLIN;
- if (events & MEMIF_FD_EVENT_WRITE)
+ if (fde.type & MEMIF_FD_EVENT_WRITE)
evt |= EPOLLOUT;
- if (events & MEMIF_FD_EVENT_MOD)
- return memif_mod_epoll_fd (lm, fd, evt);
-
- return memif_add_epoll_fd (lm, fd, evt);
-}
-
-int
-add_list_elt (libmemif_main_t * lm, memif_list_elt_t * e,
- memif_list_elt_t ** list, uint16_t * len)
-{
- memif_list_elt_t *tmp;
- int i;
-
- for (i = 0; i < *len; i++)
- {
- if ((*list)[i].data_struct == NULL)
- {
- (*list)[i].key = e->key;
- (*list)[i].data_struct = e->data_struct;
- return i;
- }
- }
-
- tmp = lm->realloc (*list, sizeof (memif_list_elt_t) * *len * 2);
- if (tmp == NULL)
- return -1;
-
- for (i = *len; i < *len * 2; i++)
- {
- tmp[i].key = -1;
- tmp[i].data_struct = NULL;
- }
-
- tmp[*len].key = e->key;
- tmp[*len].data_struct = e->data_struct;
- i = *len;
- *len = *len * 2;
- *list = tmp;
-
- return i;
-}
-
-int
-get_list_elt (memif_list_elt_t ** e, memif_list_elt_t * list, uint16_t len,
- int key)
-{
- int i;
- if (key == -1)
- {
- *e = NULL;
- return -1;
- }
-
- for (i = 0; i < len; i++)
- {
- if (list[i].key == key)
- {
- *e = &list[i];
- return 0;
- }
- }
- *e = NULL;
- return -1;
-}
-
-/* does not free memory, only marks element as free */
-int
-free_list_elt (memif_list_elt_t * list, uint16_t len, int key)
-{
- int i;
- for (i = 0; i < len; i++)
- {
- if (list[i].key == key)
- {
- list[i].key = -1;
- list[i].data_struct = NULL;
- return 0;
- }
- }
-
- return -1;
-}
-
-int
-free_list_elt_ctx (memif_list_elt_t * list, uint16_t len,
- memif_connection_t * ctx)
-{
- int i;
- for (i = 0; i < len; i++)
- {
- if (list[i].key == -1)
- {
- if (list[i].data_struct == ctx)
- {
- list[i].data_struct = NULL;
- return 0;
- }
- }
- }
+ if (fde.type & MEMIF_FD_EVENT_MOD)
+ return memif_mod_epoll_fd (ms, fde, evt);
- return -1;
+ return memif_add_epoll_fd (ms, fde, evt);
}
static void
-memif_control_fd_update_register (libmemif_main_t * lm,
- memif_control_fd_update_t * cb)
+memif_control_fd_update_register (memif_socket_t *ms,
+ memif_control_fd_update_t *cb)
{
- lm->control_fd_update = cb;
+ ms->args.on_control_fd_update = cb;
}
void
-memif_register_external_region (memif_add_external_region_t * ar,
- memif_get_external_region_addr_t * gr,
- memif_del_external_region_t * dr,
- memif_get_external_buffer_offset_t * go)
+memif_register_external_region (memif_socket_handle_t sock,
+ memif_add_external_region_t *ar,
+ memif_get_external_region_addr_t *gr,
+ memif_del_external_region_t *dr,
+ memif_get_external_buffer_offset_t *go)
{
- libmemif_main_t *lm = &libmemif_main;
- lm->add_external_region = ar;
- lm->get_external_region_addr = gr;
- lm->del_external_region = dr;
- lm->get_external_buffer_offset = go;
+ memif_socket_t *ms = (memif_socket_t *) sock;
+ ms->add_external_region = ar;
+ ms->get_external_region_addr = gr;
+ ms->del_external_region = dr;
+ ms->get_external_buffer_offset = go;
}
static void
-memif_alloc_register (libmemif_main_t * lm, memif_alloc_t * ma)
+memif_alloc_register (memif_socket_t *ms, memif_alloc_t *ma)
{
- lm->alloc = ma;
+ ms->args.alloc = ma;
}
static void
-memif_realloc_register (libmemif_main_t * lm, memif_realloc_t * mr)
+memif_realloc_register (memif_socket_t *ms, memif_realloc_t *mr)
{
- lm->realloc = mr;
+ ms->args.realloc = mr;
}
static void
-memif_free_register (libmemif_main_t * lm, memif_free_t * mf)
-{
- lm->free = mf;
-}
-
-int
-memif_set_connection_request_timer (struct itimerspec timer)
-{
- libmemif_main_t *lm = &libmemif_main;
- int err = MEMIF_ERR_SUCCESS;
-
- lm->arm = timer;
-
- /* overwrite timer, if already armed */
- if (lm->disconn_slaves != 0)
- {
- if (timerfd_settime (lm->timerfd, 0, &lm->arm, NULL) < 0)
- {
- err = memif_syscall_error_handler (errno);
- }
- }
- return err;
-}
-
-int
-memif_per_thread_set_connection_request_timer (memif_per_thread_main_handle_t
- pt_main,
- struct itimerspec timer)
-{
- libmemif_main_t *lm = (libmemif_main_t *) pt_main;
- int err = MEMIF_ERR_SUCCESS;
-
- lm->arm = timer;
-
- /* overwrite timer, if already armed */
- if (lm->disconn_slaves != 0)
- {
- if (timerfd_settime (lm->timerfd, 0, &lm->arm, NULL) < 0)
- {
- err = memif_syscall_error_handler (errno);
- }
- }
- return err;
-}
-
-int
-memif_init (memif_control_fd_update_t * on_control_fd_update, char *app_name,
- memif_alloc_t * memif_alloc, memif_realloc_t * memif_realloc,
- memif_free_t * memif_free)
+memif_free_register (memif_socket_t *ms, memif_free_t *mf)
{
- int err = MEMIF_ERR_SUCCESS; /* 0 */
- libmemif_main_t *lm = &libmemif_main;
- memset (lm, 0, sizeof (libmemif_main_t));
-
- /* register custom memory management */
- if (memif_alloc != NULL)
- {
- memif_alloc_register (lm, memif_alloc);
- }
- else
- memif_alloc_register (lm, malloc);
-
- if (memif_realloc != NULL)
- {
- memif_realloc_register (lm, memif_realloc);
- }
- else
- memif_realloc_register (lm, realloc);
-
- if (memif_free != NULL)
- memif_free_register (lm, memif_free);
- else
- memif_free_register (lm, free);
-
- if (app_name != NULL)
- {
- strlcpy ((char *) lm->app_name, app_name, sizeof (lm->app_name));
- }
- else
- {
- strlcpy ((char *) lm->app_name, MEMIF_DEFAULT_APP_NAME,
- sizeof (lm->app_name));
- }
-
- lm->poll_cancel_fd = -1;
- /* register control fd update callback */
- if (on_control_fd_update != NULL)
- memif_control_fd_update_register (lm, on_control_fd_update);
- else
- {
- lm->epfd = epoll_create (1);
- memif_control_fd_update_register (lm, memif_control_fd_update);
- if ((lm->poll_cancel_fd = eventfd (0, EFD_NONBLOCK)) < 0)
- {
- err = errno;
- DBG ("eventfd: %s", strerror (err));
- return memif_syscall_error_handler (err);
- }
- lm->control_fd_update (lm->poll_cancel_fd, MEMIF_FD_EVENT_READ, lm->private_ctx);
- DBG ("libmemif event polling initialized");
- }
-
- lm->control_list_len = 2;
- lm->interrupt_list_len = 2;
- lm->socket_list_len = 1;
- lm->pending_list_len = 1;
-
- lm->control_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->control_list_len);
- if (lm->control_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- lm->interrupt_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->interrupt_list_len);
- if (lm->interrupt_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- lm->socket_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->socket_list_len);
- if (lm->socket_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- lm->pending_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->pending_list_len);
- if (lm->pending_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
-
- int i;
- for (i = 0; i < lm->control_list_len; i++)
- {
- lm->control_list[i].key = -1;
- lm->control_list[i].data_struct = NULL;
- }
- for (i = 0; i < lm->interrupt_list_len; i++)
- {
- lm->interrupt_list[i].key = -1;
- lm->interrupt_list[i].data_struct = NULL;
- }
- for (i = 0; i < lm->socket_list_len; i++)
- {
- lm->socket_list[i].key = -1;
- lm->socket_list[i].data_struct = NULL;
- }
- for (i = 0; i < lm->pending_list_len; i++)
- {
- lm->pending_list[i].key = -1;
- lm->pending_list[i].data_struct = NULL;
- }
-
- lm->disconn_slaves = 0;
-
- lm->timerfd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK);
- if (lm->timerfd < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
-
- lm->arm.it_value.tv_sec = MEMIF_DEFAULT_RECONNECT_PERIOD_SEC;
- lm->arm.it_value.tv_nsec = MEMIF_DEFAULT_RECONNECT_PERIOD_NSEC;
- lm->arm.it_interval.tv_sec = MEMIF_DEFAULT_RECONNECT_PERIOD_SEC;
- lm->arm.it_interval.tv_nsec = MEMIF_DEFAULT_RECONNECT_PERIOD_NSEC;
-
- if (lm->control_fd_update (lm->timerfd, MEMIF_FD_EVENT_READ, lm->private_ctx) < 0)
- {
- DBG ("callback type memif_control_fd_update_t error!");
- err = MEMIF_ERR_CB_FDUPDATE;
- goto error;
- }
-
- /* Create default socket */
- err = memif_create_socket ((memif_socket_handle_t *) &
- lm->default_socket,
- MEMIF_DEFAULT_SOCKET_PATH, NULL);
- if (err != MEMIF_ERR_SUCCESS)
- goto error;
-
- return err;
-
-error:
- memif_cleanup ();
- return err;
-}
-
-int
-memif_per_thread_init (memif_per_thread_main_handle_t * pt_main,
- void *private_ctx,
- memif_control_fd_update_t * on_control_fd_update,
- char *app_name, memif_alloc_t * memif_alloc,
- memif_realloc_t * memif_realloc,
- memif_free_t * memif_free)
-{
- memif_err_t err = MEMIF_ERR_SUCCESS;
- int i;
- libmemif_main_t *lm;
-
- /* Allocate unique libmemif main */
- if (memif_alloc != NULL)
- lm = memif_alloc (sizeof (libmemif_main_t));
- else
- lm = malloc (sizeof (libmemif_main_t));
-
- if (lm == NULL)
- return MEMIF_ERR_NOMEM;
-
- memset (lm, 0, sizeof (libmemif_main_t));
-
- /* register custom memory management */
- if (memif_alloc != NULL)
- {
- memif_alloc_register (lm, memif_alloc);
- }
- else
- memif_alloc_register (lm, malloc);
-
- if (memif_realloc != NULL)
- {
- memif_realloc_register (lm, memif_realloc);
- }
- else
- memif_realloc_register (lm, realloc);
-
- if (memif_free != NULL)
- memif_free_register (lm, memif_free);
- else
- memif_free_register (lm, free);
-
- lm->private_ctx = private_ctx;
-
- /* set app name */
- if (app_name != NULL)
- {
- strlcpy ((char *) lm->app_name, app_name, MEMIF_NAME_LEN);
- }
- else
- {
- strlcpy ((char *) lm->app_name, MEMIF_DEFAULT_APP_NAME,
- sizeof (lm->app_name));
- }
-
- lm->poll_cancel_fd = -1;
- /* register control fd update callback */
- if (on_control_fd_update != NULL)
- memif_control_fd_update_register (lm, on_control_fd_update);
- else
- {
- /* private_ctx only used internally by memif_control_fd_update
- * pointer to this libmemif main
- */
- lm->private_ctx = lm;
- lm->epfd = epoll_create (1);
- memif_control_fd_update_register (lm, memif_control_fd_update);
- if ((lm->poll_cancel_fd = eventfd (0, EFD_NONBLOCK)) < 0)
- {
- err = errno;
- DBG ("eventfd: %s", strerror (err));
- return memif_syscall_error_handler (err);
- }
- lm->control_fd_update (lm->poll_cancel_fd, MEMIF_FD_EVENT_READ,
- lm->private_ctx);
- DBG ("libmemif event polling initialized");
- }
-
- /* Initialize lists */
- lm->control_list_len = 2;
- lm->interrupt_list_len = 2;
- lm->socket_list_len = 1;
- lm->pending_list_len = 1;
-
- lm->control_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->control_list_len);
- if (lm->control_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- lm->interrupt_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->interrupt_list_len);
- if (lm->interrupt_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- lm->socket_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->socket_list_len);
- if (lm->socket_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- lm->pending_list =
- lm->alloc (sizeof (memif_list_elt_t) * lm->pending_list_len);
- if (lm->pending_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
-
- for (i = 0; i < lm->control_list_len; i++)
- {
- lm->control_list[i].key = -1;
- lm->control_list[i].data_struct = NULL;
- }
- for (i = 0; i < lm->interrupt_list_len; i++)
- {
- lm->interrupt_list[i].key = -1;
- lm->interrupt_list[i].data_struct = NULL;
- }
- for (i = 0; i < lm->socket_list_len; i++)
- {
- lm->socket_list[i].key = -1;
- lm->socket_list[i].data_struct = NULL;
- }
- for (i = 0; i < lm->pending_list_len; i++)
- {
- lm->pending_list[i].key = -1;
- lm->pending_list[i].data_struct = NULL;
- }
-
- /* Initialize autoconnect */
- lm->disconn_slaves = 0;
-
- lm->timerfd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK);
- if (lm->timerfd < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
-
- lm->arm.it_value.tv_sec = MEMIF_DEFAULT_RECONNECT_PERIOD_SEC;
- lm->arm.it_value.tv_nsec = MEMIF_DEFAULT_RECONNECT_PERIOD_NSEC;
- lm->arm.it_interval.tv_sec = MEMIF_DEFAULT_RECONNECT_PERIOD_SEC;
- lm->arm.it_interval.tv_nsec = MEMIF_DEFAULT_RECONNECT_PERIOD_NSEC;
-
- if (lm->control_fd_update (lm->timerfd, MEMIF_FD_EVENT_READ,
- lm->private_ctx) < 0)
- {
- DBG ("callback type memif_control_fd_update_t error!");
- err = MEMIF_ERR_CB_FDUPDATE;
- goto error;
- }
-
- *pt_main = lm;
-
- return err;
-
-error:
- *pt_main = lm;
- memif_per_thread_cleanup (pt_main);
- return err;
+ ms->args.free = mf;
}
static inline memif_ring_t *
@@ -847,122 +397,164 @@ memif_set_rx_mode (memif_conn_handle_t c, memif_rx_mode_t rx_mode,
return MEMIF_ERR_SUCCESS;
}
-static int
-memif_socket_start_listening (memif_socket_t * ms)
+int
+memif_poll_cancel_handler (memif_fd_event_type_t type, void *private_ctx)
{
- libmemif_main_t *lm = get_libmemif_main (ms);
- memif_list_elt_t elt;
- struct stat file_stat;
- struct sockaddr_un un = { 0 };
- int on = 1;
- int err = MEMIF_ERR_SUCCESS;
+ return MEMIF_ERR_POLL_CANCEL;
+}
- if (ms->type == MEMIF_SOCKET_TYPE_CLIENT)
- return MEMIF_ERR_INVAL_ARG;
+int
+memif_connect_handler (memif_fd_event_type_t type, void *private_ctx)
+{
+ memif_socket_t *ms = (memif_socket_t *) private_ctx;
+ memif_connection_t *c;
- /* check if file exists */
- if (stat ((char *) ms->filename, &file_stat) == 0)
+ if (ms->timer_fd >= 0)
{
- if (S_ISSOCK (file_stat.st_mode))
- unlink ((char *) ms->filename);
- else
- return memif_syscall_error_handler (errno);
+ uint64_t u64;
+ ssize_t __attribute__ ((unused)) r;
+ /*
+ Have to read the timer fd else it stays read-ready
+ and makes epoll_pwait() return without sleeping
+ */
+ r = read (ms->timer_fd, &u64, sizeof (u64));
}
- ms->fd = socket (AF_UNIX, SOCK_SEQPACKET, 0);
- if (ms->fd < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
+ /* loop ms->slave_interfaces and request connection for disconnected ones */
+ TAILQ_FOREACH (c, &ms->slave_interfaces, next)
+ {
+ /* connected or connecting */
+ if (c->control_channel != NULL)
+ continue;
- DBG ("socket %d created", ms->fd);
- un.sun_family = AF_UNIX;
- strlcpy ((char *) un.sun_path, (char *) ms->filename, sizeof (un.sun_path));
- if (setsockopt (ms->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)) < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
- if (bind (ms->fd, (struct sockaddr *) &un, sizeof (un)) < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
- if (listen (ms->fd, 1) < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
- if (stat ((char *) ms->filename, &file_stat) < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
+ /* ignore errors */
+ memif_request_connection (c);
+ }
- /* add socket to libmemif main */
- elt.key = ms->fd;
- elt.data_struct = ms;
- add_list_elt (lm, &elt, &lm->socket_list, &lm->socket_list_len);
- /* if lm->private_ctx == lm event polling is done by libmemif */
- lm->control_fd_update (ms->fd, MEMIF_FD_EVENT_READ, lm->private_ctx);
+ return MEMIF_ERR_SUCCESS;
+}
- ms->type = MEMIF_SOCKET_TYPE_LISTENER;
+int
+memif_set_connection_request_timer (memif_socket_handle_t sock,
+ struct itimerspec timer)
+{
+ memif_socket_t *ms = (memif_socket_t *) sock;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ void *ctx;
- return err;
+ if (ms == NULL)
+ return MEMIF_ERR_INVAL_ARG;
-error:
- if (ms->fd > 0)
+ if (ms->timer_fd < 0)
{
- close (ms->fd);
- ms->fd = -1;
+ /* only create new timer if there is a valid interval */
+ if (timer.it_interval.tv_sec == 0 && timer.it_interval.tv_nsec == 0)
+ return MEMIF_ERR_SUCCESS;
+
+ /* create timerfd */
+ ms->timer_fd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK);
+ if (ms->timer_fd < 0)
+ return memif_syscall_error_handler (errno);
+
+ /* start listening for events */
+ fdata = ms->args.alloc (sizeof (*fdata));
+ fdata->event_handler = memif_connect_handler;
+ fdata->private_ctx = ms;
+
+ fde.fd = ms->timer_fd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
+
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
}
- return err;
+
+ ms->args.connection_request_timer = timer;
+
+ /* arm the timer */
+ if (timerfd_settime (ms->timer_fd, 0, &ms->args.connection_request_timer,
+ NULL) < 0)
+ return memif_syscall_error_handler (errno);
+
+ return MEMIF_ERR_SUCCESS;
}
int
-memif_create_socket (memif_socket_handle_t * sock, const char *filename,
+memif_create_socket (memif_socket_handle_t *sock, memif_socket_args_t *args,
void *private_ctx)
{
- libmemif_main_t *lm = &libmemif_main;
memif_socket_t *ms = (memif_socket_t *) * sock;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
int i, err = MEMIF_ERR_SUCCESS;
-
- for (i = 0; i < lm->socket_list_len; i++)
- {
- if ((ms = (memif_socket_t *) lm->socket_list[i].data_struct) != NULL)
- {
- if (strncmp ((char *) ms->filename, filename,
- strlen ((char *) ms->filename)) == 0)
- return MEMIF_ERR_INVAL_ARG;
- }
- }
+ void *ctx;
/* allocate memif_socket_t */
ms = NULL;
- ms = lm->alloc (sizeof (memif_socket_t));
+ if (args->alloc != NULL)
+ ms = args->alloc (sizeof (memif_socket_t));
+ else
+ ms = malloc (sizeof (memif_socket_t));
if (ms == NULL)
{
err = MEMIF_ERR_NOMEM;
goto error;
}
+
+ /* default values */
memset (ms, 0, sizeof (memif_socket_t));
- /* set filename */
- memset (ms->filename, 0, sizeof (ms->filename));
- strlcpy ((char *) ms->filename, filename, sizeof (ms->filename));
+ ms->epfd = -1;
+ ms->listener_fd = -1;
+ ms->poll_cancel_fd = -1;
+ ms->timer_fd = -1;
+
+ /* copy arguments to internal struct */
+ memcpy (&ms->args, args, sizeof (*args));
+ ms->private_ctx = private_ctx;
+
+ if (ms->args.alloc == NULL)
+ memif_alloc_register (ms, malloc);
+ if (ms->args.realloc == NULL)
+ memif_realloc_register (ms, realloc);
+ if (ms->args.free == NULL)
+ memif_free_register (ms, free);
+
+ TAILQ_INIT (&ms->master_interfaces);
+ TAILQ_INIT (&ms->slave_interfaces);
+
+ /* FIXME: implement connection request timer */
+
+ /* initialize internal epoll */
+ if (ms->args.on_control_fd_update == NULL)
+ {
+ ms->epfd = epoll_create (1);
+ /* register default fd update callback */
+ memif_control_fd_update_register (ms, memif_control_fd_update);
+ ms->poll_cancel_fd = eventfd (0, EFD_NONBLOCK);
+ if (ms->poll_cancel_fd < 0)
+ {
+ err = errno;
+ DBG ("eventfd: %s", strerror (err));
+ return memif_syscall_error_handler (err);
+ }
+ /* add interrupt fd to epfd */
+ fdata = ms->args.alloc (sizeof (*fdata));
+ fdata->event_handler = memif_poll_cancel_handler;
+ fdata->private_ctx = ms;
- ms->type = MEMIF_SOCKET_TYPE_NONE;
+ fde.fd = ms->poll_cancel_fd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
- ms->interface_list_len = 1;
- ms->interface_list =
- lm->alloc (sizeof (memif_list_elt_t) * ms->interface_list_len);
- if (ms->interface_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
}
- ms->interface_list[0].key = -1;
- ms->interface_list[0].data_struct = NULL;
+
+ err =
+ memif_set_connection_request_timer (ms, ms->args.connection_request_timer);
+ if (err != MEMIF_ERR_SUCCESS)
+ goto error;
*sock = ms;
@@ -971,107 +563,86 @@ memif_create_socket (memif_socket_handle_t * sock, const char *filename,
error:
if (ms != NULL)
{
- if (ms->fd > 0)
- {
- close (ms->fd);
- ms->fd = -1;
- }
- if (ms->interface_list != NULL)
- {
- lm->free (ms->interface_list);
- ms->interface_list = NULL;
- ms->interface_list_len = 0;
- }
- lm->free (ms);
- *sock = ms = NULL;
+ ms->args.free (ms);
+ if (ms->epfd != -1)
+ close (ms->epfd);
+ if (ms->poll_cancel_fd != -1)
+ close (ms->poll_cancel_fd);
}
return err;
}
-int
-memif_per_thread_create_socket (memif_per_thread_main_handle_t pt_main,
- memif_socket_handle_t * sock,
- const char *filename, void *private_ctx)
+memif_socket_handle_t
+memif_get_socket_handle (memif_conn_handle_t conn)
{
- libmemif_main_t *lm = (libmemif_main_t *) pt_main;
- memif_socket_t *ms = (memif_socket_t *) * sock;
- int i, err = MEMIF_ERR_SUCCESS;
+ memif_connection_t *c = (memif_connection_t *) conn;
- if (lm == NULL)
- return MEMIF_ERR_INVAL_ARG;
+ if (c == NULL)
+ return NULL;
- for (i = 0; i < lm->socket_list_len; i++)
- {
- if ((ms = (memif_socket_t *) lm->socket_list[i].data_struct) != NULL)
- {
- if (strncmp ((char *) ms->filename, filename,
- strlen ((char *) ms->filename)) == 0)
- return MEMIF_ERR_INVAL_ARG;
- }
- }
+ return c->args.socket;
+}
+
+const char *
+memif_get_socket_path (memif_socket_handle_t sock)
+{
+ memif_socket_t *ms = (memif_socket_t *) sock;
- /* allocate memif_socket_t */
- ms = NULL;
- ms = lm->alloc (sizeof (memif_socket_t));
if (ms == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- memset (ms, 0, sizeof (memif_socket_t));
- ms->lm = lm;
- /* set filename */
- memset (ms->filename, 0, sizeof (ms->filename));
- strlcpy ((char *) ms->filename, filename, sizeof (ms->filename));
+ return NULL;
- ms->type = MEMIF_SOCKET_TYPE_NONE;
+ return ms->args.path;
+}
- ms->interface_list_len = 1;
- ms->interface_list =
- lm->alloc (sizeof (memif_list_elt_t) * ms->interface_list_len);
- if (ms->interface_list == NULL)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
- ms->interface_list[0].key = -1;
- ms->interface_list[0].data_struct = NULL;
+int
+memif_get_listener_fd (memif_socket_handle_t sock)
+{
+ memif_socket_t *ms = (memif_socket_t *) sock;
- *sock = ms;
+ if (ms == NULL)
+ return -1;
- return err;
+ return ms->listener_fd;
+}
-error:
- if (ms != NULL)
- {
- if (ms->fd > 0)
- {
- close (ms->fd);
- ms->fd = -1;
- }
- if (ms->interface_list != NULL)
- {
- lm->free (ms->interface_list);
- ms->interface_list = NULL;
- ms->interface_list_len = 0;
- }
- lm->free (ms);
- *sock = ms = NULL;
- }
- return err;
+int
+memif_set_listener_fd (memif_socket_handle_t sock, int fd)
+{
+ memif_socket_t *ms = (memif_socket_t *) sock;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ void *ctx;
+
+ if ((ms == NULL) || (fd < 0))
+ return MEMIF_ERR_INVAL_ARG;
+
+ fdata = ms->args.alloc (sizeof (*fdata));
+ if (fdata == NULL)
+ return MEMIF_ERR_NOMEM;
+
+ ms->listener_fd = fd;
+
+ fdata->event_handler = memif_listener_handler;
+ fdata->private_ctx = ms;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ /* send fd to epoll */
+ fde.fd = ms->listener_fd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
+ ms->args.on_control_fd_update (fde, ctx);
+
+ return MEMIF_ERR_SUCCESS;
}
int
-memif_create (memif_conn_handle_t * c, memif_conn_args_t * args,
- memif_connection_update_t * on_connect,
- memif_connection_update_t * on_disconnect,
- memif_interrupt_t * on_interrupt, void *private_ctx)
+memif_create (memif_conn_handle_t *c, memif_conn_args_t *args,
+ memif_connection_update_t *on_connect,
+ memif_connection_update_t *on_disconnect,
+ memif_on_interrupt_t *on_interrupt, void *private_ctx)
{
- libmemif_main_t *lm = get_libmemif_main (args->socket);
int err, index = 0;
- memif_list_elt_t elt;
memif_connection_t *conn = (memif_connection_t *) * c;
- memif_socket_t *ms;
+ memif_socket_t *ms = (memif_socket_t *) args->socket;
if (conn != NULL)
{
@@ -1079,7 +650,13 @@ memif_create (memif_conn_handle_t * c, memif_conn_args_t * args,
return MEMIF_ERR_CONN;
}
- conn = (memif_connection_t *) lm->alloc (sizeof (memif_connection_t));
+ if (ms == NULL)
+ {
+ DBG ("Missing memif socket");
+ return MEMIF_ERR_INVAL_ARG;
+ }
+
+ conn = (memif_connection_t *) ms->args.alloc (sizeof (*conn));
if (conn == NULL)
{
err = MEMIF_ERR_NOMEM;
@@ -1109,117 +686,85 @@ memif_create (memif_conn_handle_t * c, memif_conn_args_t * args,
conn->args.log2_ring_size = args->log2_ring_size;
conn->args.is_master = args->is_master;
conn->args.mode = args->mode;
- conn->msg_queue = NULL;
+ conn->args.socket = args->socket;
conn->regions = NULL;
conn->tx_queues = NULL;
conn->rx_queues = NULL;
- conn->fd = -1;
+ conn->control_channel = NULL;
conn->on_connect = on_connect;
conn->on_disconnect = on_disconnect;
conn->on_interrupt = on_interrupt;
conn->private_ctx = private_ctx;
memset (&conn->run_args, 0, sizeof (memif_conn_run_args_t));
+ uint8_t l = sizeof (conn->args.interface_name);
strlcpy ((char *) conn->args.interface_name, (char *) args->interface_name,
- sizeof (conn->args.interface_name));
+ l);
- if ((strlen ((char *) args->secret)) > 0)
+ if ((l = strlen ((char *) args->secret)) > 0)
strlcpy ((char *) conn->args.secret, (char *) args->secret,
sizeof (conn->args.secret));
- if (args->socket != NULL)
- conn->args.socket = args->socket;
- else if (lm->default_socket != NULL)
- conn->args.socket = lm->default_socket;
+ if (args->is_master)
+ TAILQ_INSERT_TAIL (&ms->master_interfaces, conn, next);
else
- {
- err = MEMIF_ERR_INVAL_ARG;
- goto error;
- }
-
- ms = (memif_socket_t *) conn->args.socket;
+ TAILQ_INSERT_TAIL (&ms->slave_interfaces, conn, next);
- if ((conn->args.is_master && ms->type == MEMIF_SOCKET_TYPE_CLIENT) ||
- (!conn->args.is_master && ms->type == MEMIF_SOCKET_TYPE_LISTENER))
+ err = memif_request_connection (conn);
+ if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_CONNREFUSED)
{
- err = MEMIF_ERR_INVAL_ARG;
+ if (args->is_master)
+ TAILQ_REMOVE (&ms->master_interfaces, conn, next);
+ else
+ TAILQ_REMOVE (&ms->slave_interfaces, conn, next);
goto error;
}
- elt.key = conn->args.interface_id;
- elt.data_struct = conn;
- add_list_elt (lm, &elt, &ms->interface_list, &ms->interface_list_len);
- ms->use_count++;
-
- if (conn->args.is_master)
- {
- if (ms->type == MEMIF_SOCKET_TYPE_NONE)
- {
- err = memif_socket_start_listening (ms);
- if (err != MEMIF_ERR_SUCCESS)
- goto error;
- }
- }
- else
- {
- elt.key = -1;
- elt.data_struct = conn;
- if ((index =
- add_list_elt (lm, &elt, &lm->control_list,
- &lm->control_list_len)) < 0)
- {
- err = MEMIF_ERR_NOMEM;
- goto error;
- }
-
- conn->index = index;
-
- /* try connecting to master */
- err = memif_request_connection (conn);
- if ((err != MEMIF_ERR_SUCCESS) && (lm->disconn_slaves == 0))
- {
- /* connection failed, arm reconnect timer (if not armed) */
- if (timerfd_settime (lm->timerfd, 0, &lm->arm, NULL) < 0)
- {
- err = memif_syscall_error_handler (errno);
- goto error;
- }
- }
- lm->disconn_slaves++;
- }
-
*c = conn;
return 0;
error:
if (conn != NULL)
- lm->free (conn);
+ ms->args.free (conn);
*c = conn = NULL;
return err;
}
+static inline int
+memif_path_is_abstract (const char *filename)
+{
+ return (filename[0] == '@');
+}
+
int
memif_request_connection (memif_conn_handle_t c)
{
memif_connection_t *conn = (memif_connection_t *) c;
- libmemif_main_t *lm;
memif_socket_t *ms;
int err = MEMIF_ERR_SUCCESS;
int sockfd = -1;
- struct sockaddr_un sun;
+ struct sockaddr_un un = { 0 };
+ struct stat file_stat;
+ int on = 1;
+ memif_control_channel_t *cc = NULL;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata = NULL;
+ int sunlen = sizeof (un);
+ void *ctx;
if (conn == NULL)
return MEMIF_ERR_NOCONN;
ms = (memif_socket_t *) conn->args.socket;
- lm = get_libmemif_main (ms);
-
- if (conn->args.is_master || ms->type == MEMIF_SOCKET_TYPE_LISTENER)
- return MEMIF_ERR_INVAL_ARG;
- if (conn->fd > 0)
+ /* if control channel is assigned, the interface is either connected or
+ * connecting */
+ if (conn->control_channel != NULL)
return MEMIF_ERR_ALRCONN;
+ /* if interface is master and the socket is already listener we are done */
+ if (conn->args.is_master && (ms->listener_fd != -1))
+ return MEMIF_ERR_SUCCESS;
sockfd = socket (AF_UNIX, SOCK_SEQPACKET, 0);
if (sockfd < 0)
@@ -1228,334 +773,155 @@ memif_request_connection (memif_conn_handle_t c)
goto error;
}
- sun.sun_family = AF_UNIX;
+ un.sun_family = AF_UNIX;
- strlcpy (sun.sun_path, (char *) ms->filename, sizeof (sun.sun_path));
+ /* use memcpy to support abstract socket
+ * ms->args.path is already a valid socket path
+ */
+ memcpy (un.sun_path, ms->args.path, sizeof (un.sun_path) - 1);
- if (connect (sockfd, (struct sockaddr *) &sun,
- sizeof (struct sockaddr_un)) == 0)
+ /* allocate fd event data */
+ fdata = ms->args.alloc (sizeof (*fdata));
+ if (fdata == NULL)
{
- conn->fd = sockfd;
- conn->read_fn = memif_conn_fd_read_ready;
- conn->write_fn = memif_conn_fd_write_ready;
- conn->error_fn = memif_conn_fd_error;
-
- lm->control_list[conn->index].key = conn->fd;
- lm->control_fd_update (sockfd,
- MEMIF_FD_EVENT_READ |
- MEMIF_FD_EVENT_WRITE, lm->private_ctx);
-
- lm->disconn_slaves--;
- if (lm->disconn_slaves == 0)
- {
- if (timerfd_settime (lm->timerfd, 0, &lm->disarm, NULL) < 0)
- {
- err = memif_syscall_error_handler (errno);
- return err;
- }
- }
- }
- else
- {
- err = memif_syscall_error_handler (errno);
- strcpy ((char *) conn->remote_disconnect_string, memif_strerror (err));
+ err = MEMIF_ERR_NOMEM;
goto error;
}
- ms->type = MEMIF_SOCKET_TYPE_CLIENT;
-
- return err;
-
-error:
- if (sockfd > 0)
- close (sockfd);
- sockfd = -1;
- return err;
-}
-
-int
-memif_control_fd_handler (int fd, uint8_t events)
-{
- int i, err = MEMIF_ERR_SUCCESS; /* 0 */
- uint16_t num;
- memif_list_elt_t *e = NULL;
- memif_connection_t *conn;
- libmemif_main_t *lm = &libmemif_main;
- if (fd == lm->timerfd)
+ if (memif_path_is_abstract (ms->args.path))
{
- uint64_t b;
- ssize_t size;
- size = read (fd, &b, sizeof (b));
-
- if (size == -1)
- goto error;
-
- for (i = 0; i < lm->control_list_len; i++)
- {
- if ((lm->control_list[i].key < 0)
- && (lm->control_list[i].data_struct != NULL))
- {
- conn = lm->control_list[i].data_struct;
- if (conn->args.is_master)
- continue;
- err = memif_request_connection (conn);
- if (err != MEMIF_ERR_SUCCESS)
- DBG ("memif_request_connection: %s", memif_strerror (err));
- }
- }
+ /* Ensure the string is NULL terminated */
+ un.sun_path[sizeof (un.sun_path) - 1] = '\0';
+ /* sunlen is strlen(un.sun_path) + sizeof(un.sun_family) */
+ sunlen = strlen (un.sun_path) + (sizeof (un) - sizeof (un.sun_path));
+ /* Handle abstract socket by converting '@' -> '\0' */
+ un.sun_path[0] = '\0';
}
- else
+
+ if (conn->args.is_master != 0)
{
- get_list_elt (&e, lm->interrupt_list, lm->interrupt_list_len, fd);
- if (e != NULL)
+ /* Configure socket optins */
+ if (setsockopt (sockfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)) < 0)
{
- if (((memif_connection_t *) e->data_struct)->on_interrupt != NULL)
- {
- num =
- (((memif_connection_t *) e->data_struct)->
- args.is_master) ? ((memif_connection_t *) e->
- data_struct)->run_args.
- num_s2m_rings : ((memif_connection_t *) e->data_struct)->
- run_args.num_m2s_rings;
- for (i = 0; i < num; i++)
- {
- if (((memif_connection_t *) e->data_struct)->
- rx_queues[i].int_fd == fd)
- {
- ((memif_connection_t *) e->data_struct)->
- on_interrupt ((void *) e->data_struct,
- ((memif_connection_t *) e->
- data_struct)->private_ctx, i);
- return MEMIF_ERR_SUCCESS;
- }
- }
- }
- return MEMIF_ERR_SUCCESS;
+ err = memif_syscall_error_handler (errno);
+ goto error;
}
- get_list_elt (&e, lm->socket_list, lm->socket_list_len, fd);
- if (e != NULL
- && ((memif_socket_t *) e->data_struct)->type ==
- MEMIF_SOCKET_TYPE_LISTENER)
+ if (bind (sockfd, (struct sockaddr *) &un, sunlen) < 0)
{
- err =
- memif_conn_fd_accept_ready ((memif_socket_t *) e->data_struct);
- return err;
+ err = memif_syscall_error_handler (errno);
+ goto error;
}
-
- get_list_elt (&e, lm->pending_list, lm->pending_list_len, fd);
- if (e != NULL)
+ if (listen (sockfd, 1) < 0)
{
- err = memif_read_ready (lm, fd);
- return err;
+ err = memif_syscall_error_handler (errno);
+ goto error;
}
-
- get_list_elt (&e, lm->control_list, lm->control_list_len, fd);
- if (e != NULL)
+ if (!memif_path_is_abstract (ms->args.path))
{
- if (events & MEMIF_FD_EVENT_READ)
+ /* Verify that the socket was created */
+ if (stat ((char *) ms->args.path, &file_stat) < 0)
{
- err =
- ((memif_connection_t *) e->data_struct)->
- read_fn (e->data_struct);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
- }
- if (events & MEMIF_FD_EVENT_WRITE)
- {
- err =
- ((memif_connection_t *) e->data_struct)->
- write_fn (e->data_struct);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
- }
- if (events & MEMIF_FD_EVENT_ERROR)
- {
- err =
- ((memif_connection_t *) e->data_struct)->
- error_fn (e->data_struct);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
+ err = memif_syscall_error_handler (errno);
+ goto error;
}
}
- }
- return MEMIF_ERR_SUCCESS; /* 0 */
-
-error:
- return err;
-}
+ /* assign listener fd */
+ ms->listener_fd = sockfd;
-int
-memif_per_thread_control_fd_handler (memif_per_thread_main_handle_t pt_main,
- int fd, uint8_t events)
-{
- int i, err = MEMIF_ERR_SUCCESS; /* 0 */
- uint16_t num;
- memif_list_elt_t *e = NULL;
- memif_connection_t *conn;
- libmemif_main_t *lm = (libmemif_main_t *) pt_main;
-
- if (fd == lm->timerfd)
- {
- uint64_t b;
- ssize_t size;
- size = read (fd, &b, sizeof (b));
-
- if (size == -1)
- goto error;
-
- for (i = 0; i < lm->control_list_len; i++)
- {
- if ((lm->control_list[i].key < 0)
- && (lm->control_list[i].data_struct != NULL))
- {
- conn = lm->control_list[i].data_struct;
- if (conn->args.is_master)
- continue;
- err = memif_request_connection (conn);
- if (err != MEMIF_ERR_SUCCESS)
- DBG ("memif_request_connection: %s", memif_strerror (err));
- }
- }
+ fdata->event_handler = memif_listener_handler;
+ fdata->private_ctx = ms;
}
else
{
- get_list_elt (&e, lm->interrupt_list, lm->interrupt_list_len, fd);
- if (e != NULL)
+ cc = ms->args.alloc (sizeof (*cc));
+ if (cc == NULL)
{
- if (((memif_connection_t *) e->data_struct)->on_interrupt != NULL)
- {
- num =
- (((memif_connection_t *) e->data_struct)->
- args.is_master) ? ((memif_connection_t *) e->
- data_struct)->run_args.
- num_s2m_rings : ((memif_connection_t *) e->data_struct)->
- run_args.num_m2s_rings;
- for (i = 0; i < num; i++)
- {
- if (((memif_connection_t *) e->data_struct)->
- rx_queues[i].int_fd == fd)
- {
- ((memif_connection_t *) e->data_struct)->
- on_interrupt ((void *) e->data_struct,
- ((memif_connection_t *) e->
- data_struct)->private_ctx, i);
- return MEMIF_ERR_SUCCESS;
- }
- }
- }
- return MEMIF_ERR_SUCCESS;
+ err = MEMIF_ERR_NOMEM;
+ goto error;
}
- get_list_elt (&e, lm->socket_list, lm->socket_list_len, fd);
- if (e != NULL
- && ((memif_socket_t *) e->data_struct)->type ==
- MEMIF_SOCKET_TYPE_LISTENER)
+ if (connect (sockfd, (struct sockaddr *) &un, sunlen) != 0)
{
- err =
- memif_conn_fd_accept_ready ((memif_socket_t *) e->data_struct);
- return err;
+ err = MEMIF_ERR_CONNREFUSED;
+ goto error;
}
- get_list_elt (&e, lm->pending_list, lm->pending_list_len, fd);
- if (e != NULL)
- {
- err = memif_read_ready (lm, fd);
- return err;
- }
+ /* Create control channel */
+ cc->fd = sockfd;
+ cc->sock = ms;
+ cc->conn = conn;
+ TAILQ_INIT (&cc->msg_queue);
- get_list_elt (&e, lm->control_list, lm->control_list_len, fd);
- if (e != NULL)
- {
- if (events & MEMIF_FD_EVENT_READ)
- {
- err =
- ((memif_connection_t *) e->data_struct)->
- read_fn (e->data_struct);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
- }
- if (events & MEMIF_FD_EVENT_WRITE)
- {
- err =
- ((memif_connection_t *) e->data_struct)->
- write_fn (e->data_struct);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
- }
- if (events & MEMIF_FD_EVENT_ERROR)
- {
- err =
- ((memif_connection_t *) e->data_struct)->
- error_fn (e->data_struct);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
- }
- }
+ /* assign control channel to endpoint */
+ conn->control_channel = cc;
+
+ fdata->event_handler = memif_control_channel_handler;
+ fdata->private_ctx = cc;
}
- return MEMIF_ERR_SUCCESS; /* 0 */
+ /* if event polling is done internally, send memif socket as context */
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ /* send fd to epoll */
+ fde.fd = sockfd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
+ ms->args.on_control_fd_update (fde, ctx);
+
+ return err;
error:
+ if (sockfd > 0)
+ close (sockfd);
+ sockfd = -1;
+ if (fdata != NULL)
+ ms->args.free (fdata);
+ fdata = NULL;
+ if (cc != NULL)
+ ms->args.free (cc);
+ conn->control_channel = cc = NULL;
return err;
}
int
-memif_poll_event (int timeout)
+memif_control_fd_handler (void *ptr, memif_fd_event_type_t events)
{
- libmemif_main_t *lm = &libmemif_main;
- struct epoll_event evt;
- int en = 0, err = MEMIF_ERR_SUCCESS; /* 0 */
- uint32_t events = 0;
- uint64_t counter = 0;
- ssize_t r = 0;
- memset (&evt, 0, sizeof (evt));
- evt.events = EPOLLIN | EPOLLOUT;
- sigset_t sigset;
- sigemptyset (&sigset);
- en = epoll_pwait (lm->epfd, &evt, 1, timeout, &sigset);
- if (en < 0)
- {
- err = errno;
- DBG ("epoll_pwait: %s", strerror (err));
- return memif_syscall_error_handler (err);
- }
- if (en > 0)
- {
- if (evt.data.fd == lm->poll_cancel_fd)
- {
- r = read (evt.data.fd, &counter, sizeof (counter));
- if (r == -1)
- return MEMIF_ERR_DISCONNECTED;
+ memif_fd_event_data_t *fdata = (memif_fd_event_data_t *) ptr;
- return MEMIF_ERR_POLL_CANCEL;
- }
- if (evt.events & EPOLLIN)
- events |= MEMIF_FD_EVENT_READ;
- if (evt.events & EPOLLOUT)
- events |= MEMIF_FD_EVENT_WRITE;
- if (evt.events & EPOLLERR)
- events |= MEMIF_FD_EVENT_ERROR;
- err = memif_control_fd_handler (evt.data.fd, events);
- return err;
- }
- return 0;
+ if (fdata == NULL)
+ return MEMIF_ERR_INVAL_ARG;
+
+ return fdata->event_handler (events, fdata->private_ctx);
}
int
-memif_per_thread_poll_event (memif_per_thread_main_handle_t pt_main,
- int timeout)
+memif_interrupt_handler (memif_fd_event_type_t type, void *private_ctx)
{
- libmemif_main_t *lm = (libmemif_main_t *) pt_main;
+ memif_interrupt_t *idata = (memif_interrupt_t *) private_ctx;
+
+ if (idata == NULL)
+ return MEMIF_ERR_INVAL_ARG;
+
+ return idata->c->on_interrupt (idata->c, idata->c->private_ctx, idata->qid);
+}
+
+int
+memif_poll_event (memif_socket_handle_t sock, int timeout)
+{
+ memif_socket_t *ms = (memif_socket_t *) sock;
struct epoll_event evt;
int en = 0, err = MEMIF_ERR_SUCCESS; /* 0 */
- uint32_t events = 0;
+ memif_fd_event_type_t events = 0;
uint64_t counter = 0;
ssize_t r = 0;
+ sigset_t sigset;
+
+ if (ms == NULL)
+ return MEMIF_ERR_INVAL_ARG;
+
memset (&evt, 0, sizeof (evt));
evt.events = EPOLLIN | EPOLLOUT;
- sigset_t sigset;
sigemptyset (&sigset);
- en = epoll_pwait (lm->epfd, &evt, 1, timeout, &sigset);
+ en = epoll_pwait (ms->epfd, &evt, 1, timeout, &sigset);
if (en < 0)
{
err = errno;
@@ -1564,70 +930,58 @@ memif_per_thread_poll_event (memif_per_thread_main_handle_t pt_main,
}
if (en > 0)
{
- if (evt.data.fd == lm->poll_cancel_fd)
- {
- r = read (evt.data.fd, &counter, sizeof (counter));
- if (r == -1)
- return MEMIF_ERR_DISCONNECTED;
-
- return MEMIF_ERR_POLL_CANCEL;
- }
if (evt.events & EPOLLIN)
events |= MEMIF_FD_EVENT_READ;
if (evt.events & EPOLLOUT)
events |= MEMIF_FD_EVENT_WRITE;
if (evt.events & EPOLLERR)
events |= MEMIF_FD_EVENT_ERROR;
- err = memif_control_fd_handler (evt.data.fd, events);
- return err;
+ return memif_control_fd_handler (evt.data.ptr, events);
}
- return 0;
-}
-
-int
-memif_cancel_poll_event ()
-{
- libmemif_main_t *lm = &libmemif_main;
- uint64_t counter = 1;
- ssize_t w = 0;
-
- if (lm->poll_cancel_fd == -1)
- return 0;
- w = write (lm->poll_cancel_fd, &counter, sizeof (counter));
- if (w < sizeof (counter))
- return MEMIF_ERR_INT_WRITE;
-
- return 0;
+ return MEMIF_ERR_SUCCESS;
}
int
-memif_per_thread_cancel_poll_event (memif_per_thread_main_handle_t pt_main)
+memif_cancel_poll_event (memif_socket_handle_t sock)
{
- libmemif_main_t *lm = (libmemif_main_t *) pt_main;
+ memif_socket_t *ms = (memif_socket_t *) sock;
uint64_t counter = 1;
ssize_t w = 0;
- if (lm == NULL)
+ if (ms->poll_cancel_fd == -1)
return MEMIF_ERR_INVAL_ARG;
-
- if (lm->poll_cancel_fd == -1)
- return 0;
- w = write (lm->poll_cancel_fd, &counter, sizeof (counter));
+ w = write (ms->poll_cancel_fd, &counter, sizeof (counter));
if (w < sizeof (counter))
return MEMIF_ERR_INT_WRITE;
- return 0;
+ return MEMIF_ERR_SUCCESS;
}
-static void
-memif_msg_queue_free (libmemif_main_t * lm, memif_msg_queue_elt_t ** e)
+void
+memif_close_queues (memif_socket_t *ms, memif_queue_t *queues, int nqueues)
{
- if (*e == NULL)
- return;
- memif_msg_queue_free (lm, &(*e)->next);
- lm->free (*e);
- *e = NULL;
- return;
+ memif_fd_event_t fde;
+ memif_queue_t *mq;
+ void *ctx;
+
+ int i;
+ for (i = 0; i < nqueues; i++)
+ {
+ mq = &queues[i];
+ if (mq != NULL)
+ {
+ if (mq->int_fd > 0)
+ {
+ /* Stop listening for events */
+ fde.fd = mq->int_fd;
+ fde.type = MEMIF_FD_EVENT_DEL;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
+ close (mq->int_fd);
+ }
+ mq->int_fd = -1;
+ }
+ }
}
/* send disconnect msg and close interface */
@@ -1636,85 +990,43 @@ memif_disconnect_internal (memif_connection_t * c)
{
int err = MEMIF_ERR_SUCCESS, i; /* 0 */
memif_queue_t *mq;
- libmemif_main_t *lm;
- memif_list_elt_t *e;
-
- if (c == NULL)
- {
- DBG ("no connection");
- return MEMIF_ERR_NOCONN;
- }
-
- lm = get_libmemif_main (c->args.socket);
+ memif_socket_t *ms = (memif_socket_t *) c->args.socket;
+ memif_fd_event_t fde;
+ void *ctx;
c->on_disconnect ((void *) c, c->private_ctx);
- if (c->fd > 0)
- {
- memif_msg_send_disconnect (c->fd, (uint8_t *) "interface deleted", 0);
- lm->control_fd_update (c->fd, MEMIF_FD_EVENT_DEL, lm->private_ctx);
- close (c->fd);
- }
- get_list_elt (&e, lm->control_list, lm->control_list_len, c->fd);
- if (e != NULL)
- {
- if (c->args.is_master)
- free_list_elt (lm->control_list, lm->control_list_len, c->fd);
- e->key = c->fd = -1;
- }
+ /* Delete control channel */
+ if (c->control_channel != NULL)
+ memif_delete_control_channel (c->control_channel);
if (c->tx_queues != NULL)
{
- for (i = 0; i < c->tx_queues_num; i++)
- {
- mq = &c->tx_queues[i];
- if (mq != NULL)
- {
- if (mq->int_fd > 0)
- close (mq->int_fd);
- free_list_elt (lm->interrupt_list, lm->interrupt_list_len,
- mq->int_fd);
- mq->int_fd = -1;
- }
- }
- lm->free (c->tx_queues);
+ memif_close_queues (ms, c->tx_queues, c->tx_queues_num);
+ ms->args.free (c->tx_queues);
c->tx_queues = NULL;
}
c->tx_queues_num = 0;
if (c->rx_queues != NULL)
{
- for (i = 0; i < c->rx_queues_num; i++)
- {
- mq = &c->rx_queues[i];
- if (mq != NULL)
- {
- if (mq->int_fd > 0)
- {
- if (c->on_interrupt != NULL)
- lm->control_fd_update (mq->int_fd, MEMIF_FD_EVENT_DEL,
- lm->private_ctx);
- close (mq->int_fd);
- }
- free_list_elt (lm->interrupt_list, lm->interrupt_list_len,
- mq->int_fd);
- mq->int_fd = -1;
- }
- }
- lm->free (c->rx_queues);
+ memif_close_queues (ms, c->rx_queues, c->rx_queues_num);
+ ms->args.free (c->rx_queues);
c->rx_queues = NULL;
}
c->rx_queues_num = 0;
+ /* TODO: Slave reuse regions */
+
for (i = 0; i < c->regions_num; i++)
{
if (&c->regions[i] == NULL)
continue;
if (c->regions[i].is_external != 0)
{
- lm->del_external_region (c->regions[i].addr,
- c->regions[i].region_size,
- c->regions[i].fd, c->private_ctx);
+ ms->del_external_region (c->regions[i].addr,
+ c->regions[i].region_size, c->regions[i].fd,
+ c->private_ctx);
}
else
{
@@ -1725,27 +1037,12 @@ memif_disconnect_internal (memif_connection_t * c)
c->regions[i].fd = -1;
}
}
- lm->free (c->regions);
+ ms->args.free (c->regions);
c->regions = NULL;
c->regions_num = 0;
memset (&c->run_args, 0, sizeof (memif_conn_run_args_t));
- memif_msg_queue_free (lm, &c->msg_queue);
-
- if (!(c->args.is_master))
- {
- if (lm->disconn_slaves == 0)
- {
- if (timerfd_settime (lm->timerfd, 0, &lm->arm, NULL) < 0)
- {
- err = memif_syscall_error_handler (errno);
- DBG ("timerfd_settime: arm");
- }
- }
- lm->disconn_slaves++;
- }
-
return err;
}
@@ -1757,24 +1054,44 @@ memif_get_socket_filename (memif_socket_handle_t sock)
if (ms == NULL)
return NULL;
- return (char *) ms->filename;
+ return (char *) ms->args.path;
}
int
memif_delete_socket (memif_socket_handle_t * sock)
{
memif_socket_t *ms = (memif_socket_t *) * sock;
- libmemif_main_t *lm;
+ memif_fd_event_t fde;
+ void *ctx;
/* check if socket is in use */
- if (ms == NULL || ms->use_count > 0)
+ if (ms == NULL || !TAILQ_EMPTY (&ms->master_interfaces) ||
+ !TAILQ_EMPTY (&ms->slave_interfaces))
return MEMIF_ERR_INVAL_ARG;
- lm = get_libmemif_main (ms);
+ if (ms->listener_fd > 0)
+ {
+ fde.fd = ms->listener_fd;
+ fde.type = MEMIF_FD_EVENT_DEL;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
+ }
+ ms->listener_fd = -1;
- lm->free (ms->interface_list);
- ms->interface_list = NULL;
- lm->free (ms);
+ if (ms->poll_cancel_fd > 0)
+ {
+ fde.fd = ms->poll_cancel_fd;
+ fde.type = MEMIF_FD_EVENT_DEL;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
+ }
+ ms->poll_cancel_fd = -1;
+
+ if (ms->epfd > 0)
+ close (ms->epfd);
+ ms->epfd = -1;
+
+ ms->args.free (ms);
*sock = ms = NULL;
return MEMIF_ERR_SUCCESS;
@@ -1784,8 +1101,7 @@ int
memif_delete (memif_conn_handle_t * conn)
{
memif_connection_t *c = (memif_connection_t *) * conn;
- libmemif_main_t *lm;
- memif_socket_t *ms = NULL;
+ memif_socket_t *ms;
int err = MEMIF_ERR_SUCCESS;
if (c == NULL)
@@ -1794,50 +1110,17 @@ memif_delete (memif_conn_handle_t * conn)
return MEMIF_ERR_NOCONN;
}
- if (c->fd > 0)
- {
- DBG ("DISCONNECTING");
- err = memif_disconnect_internal (c);
- if (err == MEMIF_ERR_NOCONN)
- return err;
- }
-
- lm = get_libmemif_main (c->args.socket);
-
- free_list_elt_ctx (lm->control_list, lm->control_list_len, c);
+ err = memif_disconnect_internal (c);
ms = (memif_socket_t *) c->args.socket;
- ms->use_count--;
- free_list_elt (ms->interface_list, ms->interface_list_len,
- c->args.interface_id);
- if (ms->use_count <= 0)
- {
- /* stop listening on this socket */
- if (ms->type == MEMIF_SOCKET_TYPE_LISTENER)
- {
- lm->control_fd_update (ms->fd, MEMIF_FD_EVENT_DEL, lm->private_ctx);
- free_list_elt (lm->socket_list, lm->socket_list_len, ms->fd);
- close (ms->fd);
- ms->fd = -1;
- }
- /* socket not in use */
- ms->type = MEMIF_SOCKET_TYPE_NONE;
- }
- if (!c->args.is_master)
- {
- lm->disconn_slaves--;
- if (lm->disconn_slaves <= 0)
- {
- if (timerfd_settime (lm->timerfd, 0, &lm->disarm, NULL) < 0)
- {
- err = memif_syscall_error_handler (errno);
- DBG ("timerfd_settime: disarm");
- }
- }
- }
+ if (c->args.is_master)
+ TAILQ_REMOVE (&ms->master_interfaces, c, next);
+ else
+ TAILQ_REMOVE (&ms->slave_interfaces, c, next);
+ /* TODO: don't listen with empty interface queue */
- lm->free (c);
+ ms->args.free (c);
c = NULL;
*conn = c;
@@ -1847,7 +1130,7 @@ memif_delete (memif_conn_handle_t * conn)
int
memif_connect1 (memif_connection_t * c)
{
- libmemif_main_t *lm;
+ memif_socket_t *ms;
memif_region_t *mr;
memif_queue_t *mq;
int i;
@@ -1855,7 +1138,7 @@ memif_connect1 (memif_connection_t * c)
if (c == NULL)
return MEMIF_ERR_INVAL_ARG;
- lm = get_libmemif_main (c->args.socket);
+ ms = (memif_socket_t *) c->args.socket;
for (i = 0; i < c->regions_num; i++)
{
@@ -1866,11 +1149,10 @@ memif_connect1 (memif_connection_t * c)
{
if (mr->is_external)
{
- if (lm->get_external_region_addr == NULL)
+ if (ms->get_external_region_addr == NULL)
return MEMIF_ERR_INVAL_ARG;
- mr->addr =
- lm->get_external_region_addr (mr->region_size, mr->fd,
- c->private_ctx);
+ mr->addr = ms->get_external_region_addr (
+ mr->region_size, mr->fd, c->private_ctx);
}
else
{
@@ -1878,8 +1160,8 @@ memif_connect1 (memif_connection_t * c)
return MEMIF_ERR_NO_SHMFD;
if ((mr->addr =
- mmap (NULL, mr->region_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, mr->fd, 0)) == MAP_FAILED)
+ mmap (NULL, mr->region_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, mr->fd, 0)) == MAP_FAILED)
{
return memif_syscall_error_handler (errno);
}
@@ -1918,21 +1200,17 @@ memif_connect1 (memif_connection_t * c)
}
}
- lm->control_fd_update (c->fd, MEMIF_FD_EVENT_READ | MEMIF_FD_EVENT_MOD,
- lm->private_ctx);
-
return 0;
}
static inline int
-memif_add_region (libmemif_main_t * lm, memif_connection_t * conn,
- uint8_t has_buffers)
+memif_add_region (memif_connection_t *conn, uint8_t has_buffers)
{
memif_region_t *r;
+ memif_socket_t *ms = (memif_socket_t *) conn->args.socket;
- r =
- lm->realloc (conn->regions,
- sizeof (memif_region_t) * ++conn->regions_num);
+ r = ms->args.realloc (conn->regions,
+ sizeof (memif_region_t) * ++conn->regions_num);
if (r == NULL)
return MEMIF_ERR_NOMEM;
@@ -1975,10 +1253,12 @@ memif_add_region (libmemif_main_t * lm, memif_connection_t * conn,
}
static inline int
-memif_init_queues (libmemif_main_t * lm, memif_connection_t * conn)
+memif_init_queues (memif_connection_t *conn)
{
int i, j;
memif_ring_t *ring;
+ memif_socket_t *ms = (memif_socket_t *) conn->args.socket;
+ uint32_t ring_size = 1 << conn->run_args.log2_ring_size;
for (i = 0; i < conn->run_args.num_s2m_rings; i++)
{
@@ -1987,9 +1267,11 @@ memif_init_queues (libmemif_main_t * lm, memif_connection_t * conn)
ring->head = ring->tail = 0;
ring->cookie = MEMIF_COOKIE;
ring->flags = 0;
- for (j = 0; j < (1 << conn->run_args.log2_ring_size); j++)
+ uint32_t base = i;
+ uint32_t ring_offset = base * ring_size;
+ for (j = 0; j < ring_size; j++)
{
- uint16_t slot = i * (1 << conn->run_args.log2_ring_size) + j;
+ uint32_t slot = ring_offset + j;
ring->desc[j].region = 1;
ring->desc[j].offset =
conn->regions[1].buffer_offset +
@@ -2004,10 +1286,11 @@ memif_init_queues (libmemif_main_t * lm, memif_connection_t * conn)
ring->head = ring->tail = 0;
ring->cookie = MEMIF_COOKIE;
ring->flags = 0;
- for (j = 0; j < (1 << conn->run_args.log2_ring_size); j++)
+ uint32_t base = conn->run_args.num_s2m_rings + i;
+ uint32_t ring_offset = base * ring_size;
+ for (j = 0; j < ring_size; j++)
{
- uint16_t slot = (i + conn->run_args.num_s2m_rings) *
- (1 << conn->run_args.log2_ring_size) + j;
+ uint32_t slot = ring_offset + j;
ring->desc[j].region = 1;
ring->desc[j].offset =
conn->regions[1].buffer_offset +
@@ -2016,23 +1299,17 @@ memif_init_queues (libmemif_main_t * lm, memif_connection_t * conn)
}
}
memif_queue_t *mq;
- DBG ("alloc: %p", lm->alloc);
- DBG ("size: %lu", sizeof (memif_queue_t) * conn->run_args.num_s2m_rings);
- mq =
- (memif_queue_t *) lm->alloc (sizeof (memif_queue_t) *
- conn->run_args.num_s2m_rings);
+ mq = (memif_queue_t *) ms->args.alloc (sizeof (memif_queue_t) *
+ conn->run_args.num_s2m_rings);
if (mq == NULL)
return MEMIF_ERR_NOMEM;
int x;
- memif_list_elt_t e;
+
for (x = 0; x < conn->run_args.num_s2m_rings; x++)
{
if ((mq[x].int_fd = eventfd (0, EFD_NONBLOCK)) < 0)
return memif_syscall_error_handler (errno);
- e.key = mq[x].int_fd;
- e.data_struct = conn;
- add_list_elt (lm, &e, &lm->interrupt_list, &lm->interrupt_list_len);
mq[x].ring = memif_get_ring (conn, MEMIF_RING_S2M, x);
DBG ("RING: %p I: %d", mq[x].ring, x);
@@ -2046,9 +1323,8 @@ memif_init_queues (libmemif_main_t * lm, memif_connection_t * conn)
conn->tx_queues = mq;
conn->tx_queues_num = conn->run_args.num_s2m_rings;
- mq =
- (memif_queue_t *) lm->alloc (sizeof (memif_queue_t) *
- conn->run_args.num_m2s_rings);
+ mq = (memif_queue_t *) ms->args.alloc (sizeof (memif_queue_t) *
+ conn->run_args.num_m2s_rings);
if (mq == NULL)
return MEMIF_ERR_NOMEM;
@@ -2056,9 +1332,6 @@ memif_init_queues (libmemif_main_t * lm, memif_connection_t * conn)
{
if ((mq[x].int_fd = eventfd (0, EFD_NONBLOCK)) < 0)
return memif_syscall_error_handler (errno);
- e.key = mq[x].int_fd;
- e.data_struct = conn;
- add_list_elt (lm, &e, &lm->interrupt_list, &lm->interrupt_list_len);
mq[x].ring = memif_get_ring (conn, MEMIF_RING_M2S, x);
DBG ("RING: %p I: %d", mq[x].ring, x);
@@ -2079,23 +1352,16 @@ int
memif_init_regions_and_queues (memif_connection_t * conn)
{
memif_region_t *r;
- libmemif_main_t *lm;
-
- if (conn == NULL)
- return MEMIF_ERR_INVAL_ARG;
-
- lm = get_libmemif_main (conn->args.socket);
+ memif_socket_t *ms = (memif_socket_t *) conn->args.socket;
/* region 0. rings */
- memif_add_region (lm, conn, /* has_buffers */ 0);
+ memif_add_region (conn, /* has_buffers */ 0);
/* region 1. buffers */
- if (lm->add_external_region)
+ if (ms->add_external_region)
{
- r =
- (memif_region_t *) lm->realloc (conn->regions,
- sizeof (memif_region_t) *
- ++conn->regions_num);
+ r = (memif_region_t *) ms->args.realloc (
+ conn->regions, sizeof (memif_region_t) * ++conn->regions_num);
if (r == NULL)
return MEMIF_ERR_NOMEM;
conn->regions = r;
@@ -2104,17 +1370,17 @@ memif_init_regions_and_queues (memif_connection_t * conn)
conn->run_args.buffer_size * (1 << conn->run_args.log2_ring_size) *
(conn->run_args.num_s2m_rings + conn->run_args.num_m2s_rings);
conn->regions[1].buffer_offset = 0;
- lm->add_external_region (&conn->regions[1].addr,
+ ms->add_external_region (&conn->regions[1].addr,
conn->regions[1].region_size,
&conn->regions[1].fd, conn->private_ctx);
conn->regions[1].is_external = 1;
}
else
{
- memif_add_region (lm, conn, 1);
+ memif_add_region (conn, 1);
}
- memif_init_queues (lm, conn);
+ memif_init_queues (conn);
return 0;
}
@@ -2208,7 +1474,7 @@ memif_buffer_enq_tx (memif_conn_handle_t conn, uint16_t qid,
memif_connection_t *c = (memif_connection_t *) conn;
if (EXPECT_FALSE (c == NULL))
return MEMIF_ERR_NOCONN;
- if (EXPECT_FALSE (c->fd < 0))
+ if (EXPECT_FALSE (c->control_channel == NULL))
return MEMIF_ERR_DISCONNECTED;
if (EXPECT_FALSE (qid >= c->tx_queues_num))
return MEMIF_ERR_QID;
@@ -2269,7 +1535,7 @@ memif_buffer_alloc (memif_conn_handle_t conn, uint16_t qid,
memif_connection_t *c = (memif_connection_t *) conn;
if (EXPECT_FALSE (c == NULL))
return MEMIF_ERR_NOCONN;
- if (EXPECT_FALSE (c->fd < 0))
+ if (EXPECT_FALSE (c->control_channel == NULL))
return MEMIF_ERR_DISCONNECTED;
uint8_t num =
(c->args.is_master) ? c->run_args.num_m2s_rings : c->
@@ -2279,17 +1545,16 @@ memif_buffer_alloc (memif_conn_handle_t conn, uint16_t qid,
if (EXPECT_FALSE (!count_out))
return MEMIF_ERR_INVAL_ARG;
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
+ memif_socket_t *ms = (memif_socket_t *) c->args.socket;
memif_queue_t *mq = &c->tx_queues[qid];
memif_ring_t *ring = mq->ring;
memif_buffer_t *b0;
uint16_t mask = (1 << mq->log2_ring_size) - 1;
- uint32_t offset_mask = c->run_args.buffer_size - 1;
uint16_t ring_size;
uint16_t ns;
int err = MEMIF_ERR_SUCCESS; /* 0 */
uint16_t dst_left, src_left;
- uint16_t saved_count;
+ uint16_t saved_count_out, delta_count;
uint16_t saved_next_buf;
memif_buffer_t *saved_b;
*count_out = 0;
@@ -2306,11 +1571,12 @@ memif_buffer_alloc (memif_conn_handle_t conn, uint16_t qid,
b0 = (bufs + *count_out);
saved_b = b0;
- saved_count = count;
+ saved_count_out = *count_out;
saved_next_buf = mq->next_buf;
b0->desc_index = mq->next_buf;
ring->desc[mq->next_buf & mask].flags = 0;
+ b0->flags = 0;
/* slave can produce buffer with original length */
dst_left = (c->args.is_master) ? ring->desc[mq->next_buf & mask].length :
@@ -2321,12 +1587,8 @@ memif_buffer_alloc (memif_conn_handle_t conn, uint16_t qid,
{
if (EXPECT_FALSE (dst_left == 0))
{
- if (count && ns)
+ if (ns)
{
- *count_out += 1;
- mq->next_buf++;
- ns--;
-
ring->desc[b0->desc_index & mask].flags |=
MEMIF_DESC_FLAG_NEXT;
b0->flags |= MEMIF_BUFFER_FLAG_NEXT;
@@ -2337,13 +1599,14 @@ memif_buffer_alloc (memif_conn_handle_t conn, uint16_t qid,
ring->desc[mq->next_buf & mask].length :
c->run_args.buffer_size;
ring->desc[mq->next_buf & mask].flags = 0;
+ b0->flags = 0;
}
else
{
/* rollback allocated chain buffers */
- memset (saved_b, 0, sizeof (memif_buffer_t)
- * (saved_count - count + 1));
- *count_out -= saved_count - count;
+ delta_count = *count_out - saved_count_out;
+ memset (saved_b, 0, sizeof (memif_buffer_t) * delta_count);
+ *count_out -= delta_count;
mq->next_buf = saved_next_buf;
goto no_ns;
}
@@ -2354,27 +1617,26 @@ memif_buffer_alloc (memif_conn_handle_t conn, uint16_t qid,
if (c->args.is_master == 0)
{
memif_desc_t *d = &ring->desc[mq->next_buf & mask];
- if (lm->get_external_buffer_offset)
- d->offset = lm->get_external_buffer_offset (c->private_ctx);
+ if (ms->get_external_buffer_offset)
+ d->offset = ms->get_external_buffer_offset (c->private_ctx);
else
- d->offset = d->offset - (d->offset & offset_mask);
+ d->offset = d->offset - (d->offset % c->run_args.buffer_size);
}
b0->data = memif_get_buffer (c, ring, mq->next_buf & mask);
src_left -= b0->len;
dst_left -= b0->len;
+ *count_out += 1;
+ mq->next_buf++;
+ ns--;
}
-
- *count_out += 1;
- mq->next_buf++;
- ns--;
count--;
}
no_ns:
- DBG ("allocated: %u/%u bufs. Next buffer pointer %d", *count_out, count,
- mq->next_buf);
+ DBG ("allocated: %u/%u bufs, size: %u. Next buffer pointer %d", *count_out,
+ count, size, mq->next_buf);
if (count)
{
@@ -2392,18 +1654,17 @@ memif_refill_queue (memif_conn_handle_t conn, uint16_t qid, uint16_t count,
memif_connection_t *c = (memif_connection_t *) conn;
if (EXPECT_FALSE (c == NULL))
return MEMIF_ERR_NOCONN;
- if (EXPECT_FALSE (c->fd < 0))
+ if (EXPECT_FALSE (c->control_channel == NULL))
return MEMIF_ERR_DISCONNECTED;
uint8_t num =
(c->args.is_master) ? c->run_args.num_s2m_rings : c->
run_args.num_m2s_rings;
if (EXPECT_FALSE (qid >= num))
return MEMIF_ERR_QID;
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
+ memif_socket_t *ms = (memif_socket_t *) c->args.socket;
memif_queue_t *mq = &c->rx_queues[qid];
memif_ring_t *ring = mq->ring;
uint16_t mask = (1 << mq->log2_ring_size) - 1;
- uint32_t offset_mask = c->run_args.buffer_size - 1;
uint16_t slot, counter = 0;
if (c->args.is_master)
@@ -2426,10 +1687,11 @@ memif_refill_queue (memif_conn_handle_t conn, uint16_t qid, uint16_t count,
d = &ring->desc[slot & mask];
d->region = 1;
d->length = c->run_args.buffer_size - headroom;
- if (lm->get_external_buffer_offset)
- d->offset = lm->get_external_buffer_offset (c->private_ctx);
+ if (ms->get_external_buffer_offset)
+ d->offset = ms->get_external_buffer_offset (c->private_ctx);
else
- d->offset = d->offset - (d->offset & offset_mask) + headroom;
+ d->offset =
+ d->offset - (d->offset % c->run_args.buffer_size) + headroom;
slot++;
counter++;
}
@@ -2447,7 +1709,7 @@ memif_tx_burst (memif_conn_handle_t conn, uint16_t qid,
memif_connection_t *c = (memif_connection_t *) conn;
if (EXPECT_FALSE (c == NULL))
return MEMIF_ERR_NOCONN;
- if (EXPECT_FALSE (c->fd < 0))
+ if (EXPECT_FALSE (c->control_channel == NULL))
return MEMIF_ERR_DISCONNECTED;
uint8_t num =
(c->args.is_master) ? c->run_args.num_m2s_rings : c->
@@ -2460,7 +1722,6 @@ memif_tx_burst (memif_conn_handle_t conn, uint16_t qid,
memif_queue_t *mq = &c->tx_queues[qid];
memif_ring_t *ring = mq->ring;
uint16_t mask = (1 << mq->log2_ring_size) - 1;
- uint32_t offset_mask = c->run_args.buffer_size - 1;
memif_buffer_t *b0;
memif_desc_t *d;
int64_t data_offset;
@@ -2488,10 +1749,12 @@ memif_tx_burst (memif_conn_handle_t conn, uint16_t qid,
}
d = &ring->desc[b0->desc_index & mask];
d->length = b0->len;
+ d->flags =
+ ((b0->flags & MEMIF_BUFFER_FLAG_NEXT) == 1) ? MEMIF_DESC_FLAG_NEXT : 0;
if (!c->args.is_master)
{
// reset headroom
- d->offset = d->offset - (d->offset & offset_mask);
+ d->offset = d->offset - (d->offset % c->run_args.buffer_size);
// calculate offset from user data
data_offset = b0->data - (d->offset + c->regions[d->region].addr);
if (data_offset != 0)
@@ -2500,7 +1763,7 @@ memif_tx_burst (memif_conn_handle_t conn, uint16_t qid,
if ((data_offset < 0) ||
((data_offset + b0->len) > c->run_args.buffer_size))
{
- DBG ("slot: %d, data_offset: %d, length: %d",
+ DBG ("slot: %d, data_offset: %ld, length: %d",
b0->desc_index & mask, data_offset, b0->len);
err = MEMIF_ERR_INVAL_ARG;
goto done;
@@ -2548,7 +1811,7 @@ memif_rx_burst (memif_conn_handle_t conn, uint16_t qid,
memif_connection_t *c = (memif_connection_t *) conn;
if (EXPECT_FALSE (c == NULL))
return MEMIF_ERR_NOCONN;
- if (EXPECT_FALSE (c->fd < 0))
+ if (EXPECT_FALSE (c->control_channel == NULL))
return MEMIF_ERR_DISCONNECTED;
uint8_t num =
(c->args.is_master) ? c->run_args.num_s2m_rings : c->
@@ -2589,6 +1852,7 @@ memif_rx_burst (memif_conn_handle_t conn, uint16_t qid,
b0->desc_index = cur_slot;
b0->data = memif_get_buffer (c, ring, cur_slot & mask);
b0->len = ring->desc[cur_slot & mask].length;
+ b0->flags = 0;
/* slave resets buffer length */
if (c->args.is_master == 0)
{
@@ -2638,7 +1902,6 @@ memif_get_details (memif_conn_handle_t conn, memif_details_t * md,
char *buf, ssize_t buflen)
{
memif_connection_t *c = (memif_connection_t *) conn;
- libmemif_main_t *lm;
memif_socket_t *ms;
int err = MEMIF_ERR_SUCCESS, i;
ssize_t l0 = 0, l1;
@@ -2647,7 +1910,6 @@ memif_get_details (memif_conn_handle_t conn, memif_details_t * md,
return MEMIF_ERR_NOCONN;
ms = (memif_socket_t *) c->args.socket;
- lm = get_libmemif_main (ms);
l1 = strlen ((char *) c->args.interface_name);
if (l0 + l1 < buflen)
@@ -2659,10 +1921,11 @@ memif_get_details (memif_conn_handle_t conn, memif_details_t * md,
else
err = MEMIF_ERR_NOBUF_DET;
- l1 = strlen ((char *) lm->app_name);
+ l1 = strlen ((char *) ms->args.app_name);
if (l0 + l1 < buflen)
{
- md->inst_name = (uint8_t *) strcpy (buf + l0, (char *) lm->app_name);
+ md->inst_name =
+ (uint8_t *) strcpy (buf + l0, (char *) ms->args.app_name);
l0 += l1 + 1;
}
else
@@ -2705,12 +1968,11 @@ memif_get_details (memif_conn_handle_t conn, memif_details_t * md,
md->role = (c->args.is_master) ? 0 : 1;
md->mode = c->args.mode;
- l1 = strlen ((char *) ms->filename);
+ l1 = 108;
if (l0 + l1 < buflen)
{
- md->socket_filename =
- (uint8_t *) strcpy (buf + l0, (char *) ms->filename);
- l0 += l1 + 1;
+ md->socket_path = (uint8_t *) memcpy (buf + l0, ms->args.path, 108);
+ l0 += l1;
}
else
err = MEMIF_ERR_NOBUF_DET;
@@ -2789,7 +2051,9 @@ memif_get_details (memif_conn_handle_t conn, memif_details_t * md,
else
err = MEMIF_ERR_NOBUF_DET;
- md->link_up_down = (c->fd > 0) ? 1 : 0;
+ /* This is not completely true, clients should relay on
+ * on_connect/on_disconnect callbacks */
+ md->link_up_down = (c->control_channel != NULL) ? 1 : 0;
return err; /* 0 */
}
@@ -2803,7 +2067,7 @@ memif_get_queue_efd (memif_conn_handle_t conn, uint16_t qid, int *efd)
*efd = -1;
if (c == NULL)
return MEMIF_ERR_NOCONN;
- if (c->fd < 0)
+ if (c->control_channel == NULL)
return MEMIF_ERR_DISCONNECTED;
num =
@@ -2816,63 +2080,3 @@ memif_get_queue_efd (memif_conn_handle_t conn, uint16_t qid, int *efd)
return MEMIF_ERR_SUCCESS;
}
-
-int
-memif_cleanup ()
-{
- libmemif_main_t *lm = &libmemif_main;
- int err;
-
- err = memif_delete_socket ((memif_socket_handle_t *) & lm->default_socket);
- if (err != MEMIF_ERR_SUCCESS)
- return err;
-
- if (lm->control_list)
- lm->free (lm->control_list);
- lm->control_list = NULL;
- if (lm->interrupt_list)
- lm->free (lm->interrupt_list);
- lm->interrupt_list = NULL;
- if (lm->socket_list)
- lm->free (lm->socket_list);
- lm->socket_list = NULL;
- if (lm->pending_list)
- lm->free (lm->pending_list);
- lm->pending_list = NULL;
- if (lm->poll_cancel_fd != -1)
- close (lm->poll_cancel_fd);
-
- return MEMIF_ERR_SUCCESS; /* 0 */
-}
-
-int
-memif_per_thread_cleanup (memif_per_thread_main_handle_t * pt_main)
-{
- libmemif_main_t *lm = (libmemif_main_t *) * pt_main;
-
- if (lm == NULL)
- return MEMIF_ERR_INVAL_ARG;
-
- /* No default socket in case of per thread */
-
- if (lm->control_list)
- lm->free (lm->control_list);
- lm->control_list = NULL;
- if (lm->interrupt_list)
- lm->free (lm->interrupt_list);
- lm->interrupt_list = NULL;
- if (lm->socket_list)
- lm->free (lm->socket_list);
- lm->socket_list = NULL;
- if (lm->pending_list)
- lm->free (lm->pending_list);
- lm->pending_list = NULL;
- if (lm->poll_cancel_fd != -1)
- close (lm->poll_cancel_fd);
-
- lm->free (lm);
-
- *pt_main = NULL;
-
- return MEMIF_ERR_SUCCESS; /* 0 */
-}
diff --git a/extras/libmemif/src/memif_private.h b/extras/libmemif/src/memif_private.h
index 9542936983e..71a4bc879f4 100644
--- a/extras/libmemif/src/memif_private.h
+++ b/extras/libmemif/src/memif_private.h
@@ -27,6 +27,7 @@
#include <limits.h>
#include <sys/timerfd.h>
#include <string.h>
+#include <sys/queue.h>
#include <memif.h>
#include <libmemif.h>
@@ -103,7 +104,7 @@ typedef enum
typedef struct
{
void *addr;
- uint32_t region_size;
+ memif_region_size_t region_size;
uint32_t buffer_offset;
int fd;
uint8_t is_external;
@@ -125,20 +126,10 @@ typedef struct
uint32_t next_buf; /* points to next free buffer */
} memif_queue_t;
-typedef struct memif_msg_queue_elt
-{
- memif_msg_t msg;
- int fd;
- struct memif_msg_queue_elt *next;
-} memif_msg_queue_elt_t;
-
struct memif_connection;
typedef struct memif_connection memif_connection_t;
-/* functions called by memif_control_fd_handler */
-typedef int (memif_fn) (memif_connection_t * conn);
-
typedef struct
{
uint8_t num_s2m_rings;
@@ -147,7 +138,7 @@ typedef struct
memif_log2_ring_size_t log2_ring_size;
} memif_conn_run_args_t;
-struct libmemif_main;
+struct memif_control_channel;
typedef struct memif_connection
{
@@ -155,17 +146,12 @@ typedef struct memif_connection
memif_conn_args_t args;
memif_conn_run_args_t run_args;
- int fd;
-
- memif_fn *write_fn, *read_fn, *error_fn;
+ struct memif_control_channel *control_channel;
memif_connection_update_t *on_connect, *on_disconnect;
- memif_interrupt_t *on_interrupt;
+ memif_on_interrupt_t *on_interrupt;
void *private_ctx;
- /* connection message queue */
- memif_msg_queue_elt_t *msg_queue;
-
uint8_t remote_if_name[MEMIF_NAME_LEN];
uint8_t remote_name[MEMIF_NAME_LEN];
uint8_t remote_disconnect_string[96];
@@ -180,61 +166,85 @@ typedef struct memif_connection
uint16_t flags;
#define MEMIF_CONNECTION_FLAG_WRITE (1 << 0)
+
+ TAILQ_ENTRY (memif_connection) next;
} memif_connection_t;
-typedef struct
+/** \brief Memif message queue element
+ * @param msg - memif control message (defined in memif.h)
+ * @param nex - tailq entry
+ * @param fd - File descriptor to be shared with peer endpoint
+ */
+typedef struct memif_msg_queue_elt
{
- int key;
- void *data_struct;
-} memif_list_elt_t;
+ memif_msg_t msg;
+ TAILQ_ENTRY (memif_msg_queue_elt) next;
+ int fd;
+} memif_msg_queue_elt_t;
-typedef struct
+struct memif_socket;
+
+/** \brief Memif control channel
+ * @param fd - fd used for communbication
+ * @param msg_queue - message queue
+ * @param conn - memif connection using this control channel
+ * @param sock - socket this control channel belongs to
+ *
+ * Memif controll channel represents one end of communication between two memif
+ * endpoints. The controll channel is responsible for receiving and
+ * transmitting memif control messages via UNIX domain socket.
+ */
+typedef struct memif_control_channel
{
int fd;
- uint16_t use_count;
- memif_socket_type_t type;
- uint8_t filename[108];
- /* unique database */
- struct libmemif_main *lm;
- uint16_t interface_list_len;
- void *private_ctx;
- memif_list_elt_t *interface_list; /* memif master interfaces listening on this socket */
-} memif_socket_t;
-
-typedef struct libmemif_main
+ TAILQ_HEAD (, memif_msg_queue_elt) msg_queue;
+ memif_connection_t *conn;
+ struct memif_socket *sock;
+} memif_control_channel_t;
+
+/** \brief Memif socket
+ * @param args - memif socket arguments (from libmemif.h)
+ * @param epfd - epoll fd, used for internal fd polling
+ * @param poll_cancel_fd - if event is received on this fd, interrupt polling
+ * @param listener_fd - listener fd if this socket is listener else -1
+ * @param private_ctx - private context
+ * @param master_interfaces - master interface queue
+ * @param slave_interfaces - slave interface queue
+ * @param control_channels - controll channel queue
+ */
+typedef struct memif_socket
{
- memif_control_fd_update_t *control_fd_update;
- int timerfd;
+ memif_socket_args_t args;
int epfd;
int poll_cancel_fd;
- struct itimerspec arm, disarm;
- uint16_t disconn_slaves;
- uint8_t app_name[MEMIF_NAME_LEN];
-
+ int listener_fd;
+ int timer_fd;
+ struct itimerspec timer;
void *private_ctx;
+ TAILQ_HEAD (, memif_connection) master_interfaces;
+ TAILQ_HEAD (, memif_connection) slave_interfaces;
- memif_socket_handle_t default_socket;
-
+ /* External region callbacks */
memif_add_external_region_t *add_external_region;
memif_get_external_region_addr_t *get_external_region_addr;
memif_del_external_region_t *del_external_region;
memif_get_external_buffer_offset_t *get_external_buffer_offset;
+} memif_socket_t;
- memif_alloc_t *alloc;
- memif_realloc_t *realloc;
- memif_free_t *free;
+typedef int (memif_fd_event_handler_t) (memif_fd_event_type_t type,
+ void *private_ctx);
- uint16_t control_list_len;
- uint16_t interrupt_list_len;
- uint16_t socket_list_len;
- uint16_t pending_list_len;
- memif_list_elt_t *control_list;
- memif_list_elt_t *interrupt_list;
- memif_list_elt_t *socket_list;
- memif_list_elt_t *pending_list;
-} libmemif_main_t;
+typedef struct memif_fd_event_data
+{
+ memif_fd_event_handler_t *event_handler;
+ void *private_ctx;
+} memif_fd_event_data_t;
-extern libmemif_main_t libmemif_main;
+typedef struct memif_interrupt
+{
+ memif_connection_t *c;
+ uint16_t qid;
+} memif_interrupt_t;
/* main.c */
@@ -246,19 +256,11 @@ int memif_init_regions_and_queues (memif_connection_t * c);
int memif_disconnect_internal (memif_connection_t * c);
+int memif_interrupt_handler (memif_fd_event_type_t type, void *private_ctx);
+
/* map errno to memif error code */
int memif_syscall_error_handler (int err_code);
-int add_list_elt (libmemif_main_t *lm, memif_list_elt_t * e, memif_list_elt_t ** list,
- uint16_t * len);
-
-int get_list_elt (memif_list_elt_t ** e, memif_list_elt_t * list,
- uint16_t len, int key);
-
-int free_list_elt (memif_list_elt_t * list, uint16_t len, int key);
-
-libmemif_main_t *get_libmemif_main (memif_socket_t * ms);
-
#ifndef __NR_memfd_create
#if defined __x86_64__
#define __NR_memfd_create 319
diff --git a/extras/libmemif/src/socket.c b/extras/libmemif/src/socket.c
index b801cac75ba..eaf9d2f7ca2 100644
--- a/extras/libmemif/src/socket.c
+++ b/extras/libmemif/src/socket.c
@@ -35,20 +35,27 @@
#include <memif_private.h>
/* sends msg to socket */
-static_fn int
-memif_msg_send (int fd, memif_msg_t * msg, int afd)
+static int
+memif_msg_send_from_queue (memif_control_channel_t *cc)
{
struct msghdr mh = { 0 };
struct iovec iov[1];
char ctl[CMSG_SPACE (sizeof (int))];
int rv, err = MEMIF_ERR_SUCCESS; /* 0 */
+ memif_msg_queue_elt_t *e;
- iov[0].iov_base = (void *) msg;
+ /* Pick the first message */
+ e = TAILQ_FIRST (&cc->msg_queue);
+ if (e == NULL)
+ return MEMIF_ERR_SUCCESS;
+
+ /* Construct the message */
+ iov[0].iov_base = (void *) &e->msg;
iov[0].iov_len = sizeof (memif_msg_t);
mh.msg_iov = iov;
mh.msg_iovlen = 1;
- if (afd > 0)
+ if (e->fd > 0)
{
struct cmsghdr *cmsg;
memset (&ctl, 0, sizeof (ctl));
@@ -58,52 +65,50 @@ memif_msg_send (int fd, memif_msg_t * msg, int afd)
cmsg->cmsg_len = CMSG_LEN (sizeof (int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
- memcpy (CMSG_DATA (cmsg), &afd, sizeof (int));
+ memcpy (CMSG_DATA (cmsg), &e->fd, sizeof (int));
}
- rv = sendmsg (fd, &mh, 0);
+ rv = sendmsg (cc->fd, &mh, 0);
if (rv < 0)
err = memif_syscall_error_handler (errno);
- DBG ("Message type %u sent", msg->type);
+ DBG ("Message type %u sent", e->msg.type);
+
+ /* If sent successfully, remove the msg from queue */
+ if (err == MEMIF_ERR_SUCCESS)
+ {
+ TAILQ_REMOVE (&cc->msg_queue, e, next);
+ cc->sock->args.free (e);
+ }
+
return err;
}
-/* response from memif master - master is ready to handle next message */
-static_fn int
-memif_msg_enq_ack (memif_connection_t * c)
+static memif_msg_queue_elt_t *
+memif_msg_enq (memif_control_channel_t *cc)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_msg_queue_elt_t *e =
- (memif_msg_queue_elt_t *) lm->alloc (sizeof (memif_msg_queue_elt_t));
+ memif_msg_queue_elt_t *e;
+
+ e = cc->sock->args.alloc (sizeof (*e));
if (e == NULL)
- return memif_syscall_error_handler (errno);
+ return NULL;
- memset (&e->msg, 0, sizeof (e->msg));
- e->msg.type = MEMIF_MSG_TYPE_ACK;
e->fd = -1;
+ TAILQ_INSERT_TAIL (&cc->msg_queue, e, next);
- e->next = NULL;
- if (c->msg_queue == NULL)
- {
- c->msg_queue = e;
- return MEMIF_ERR_SUCCESS; /* 0 */
- }
-
- memif_msg_queue_elt_t *cur = c->msg_queue;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = e;
-
- return MEMIF_ERR_SUCCESS; /* 0 */
+ return e;
}
-static_fn int
-memif_msg_send_hello (libmemif_main_t * lm, int fd)
+static int
+memif_msg_enq_hello (memif_control_channel_t *cc)
{
- memif_msg_t msg = { 0 };
- memif_msg_hello_t *h = &msg.hello;
- msg.type = MEMIF_MSG_TYPE_HELLO;
+ memif_msg_hello_t *h;
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
+
+ if (e == NULL)
+ return MEMIF_ERR_NOMEM;
+
+ e->msg.type = MEMIF_MSG_TYPE_HELLO;
+
+ h = &e->msg.hello;
h->min_version = MEMIF_VERSION;
h->max_version = MEMIF_VERSION;
h->max_s2m_ring = MEMIF_MAX_S2M_RING;
@@ -111,67 +116,63 @@ memif_msg_send_hello (libmemif_main_t * lm, int fd)
h->max_region = MEMIF_MAX_REGION;
h->max_log2_ring_size = MEMIF_MAX_LOG2_RING_SIZE;
- strlcpy ((char *) h->name, (char *) lm->app_name, sizeof (h->name));
+ strlcpy ((char *) h->name, (char *) cc->sock->args.app_name,
+ sizeof (h->name));
- /* msg hello is not enqueued but sent directly,
- because it is the first msg to be sent */
- return memif_msg_send (fd, &msg, -1);
+ return MEMIF_ERR_SUCCESS;
+}
+
+/* response from memif master - master is ready to handle next message */
+static int
+memif_msg_enq_ack (memif_control_channel_t *cc)
+{
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
+
+ if (e == NULL)
+ return MEMIF_ERR_NOMEM;
+
+ e->msg.type = MEMIF_MSG_TYPE_ACK;
+ e->fd = -1;
+
+ return MEMIF_ERR_SUCCESS; /* 0 */
}
/* send id and secret (optional) for interface identification */
-static_fn int
-memif_msg_enq_init (memif_connection_t * c)
+static int
+memif_msg_enq_init (memif_control_channel_t *cc)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_msg_queue_elt_t *e =
- (memif_msg_queue_elt_t *) lm->alloc (sizeof (memif_msg_queue_elt_t));
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
+
if (e == NULL)
- return memif_syscall_error_handler (errno);
- memset (e, 0, sizeof (memif_msg_queue_elt_t));
+ return MEMIF_ERR_NOMEM;
- memset (&e->msg, 0, sizeof (e->msg));
memif_msg_init_t *i = &e->msg.init;
e->msg.type = MEMIF_MSG_TYPE_INIT;
e->fd = -1;
i->version = MEMIF_VERSION;
- i->id = c->args.interface_id;
- i->mode = c->args.mode;
-
- strlcpy ((char *) i->name, (char *) lm->app_name, sizeof (i->name));
- if (strlen ((char *) c->args.secret) > 0)
- strncpy ((char *) i->secret, (char *) c->args.secret, sizeof (i->secret));
-
- e->next = NULL;
- if (c->msg_queue == NULL)
- {
- c->msg_queue = e;
- return MEMIF_ERR_SUCCESS; /* 0 */
- }
+ i->id = cc->conn->args.interface_id;
+ i->mode = cc->conn->args.mode;
- memif_msg_queue_elt_t *cur = c->msg_queue;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = e;
+ strlcpy ((char *) i->name, (char *) cc->sock->args.app_name,
+ sizeof (i->name));
+ if (strlen ((char *) cc->conn->args.secret) > 0)
+ strlcpy ((char *) i->secret, (char *) cc->conn->args.secret,
+ sizeof (i->secret));
return MEMIF_ERR_SUCCESS; /* 0 */
}
/* send information about region specified by region_index */
-static_fn int
-memif_msg_enq_add_region (memif_connection_t * c, uint8_t region_index)
+static int
+memif_msg_enq_add_region (memif_control_channel_t *cc, uint8_t region_index)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_region_t *mr = &c->regions[region_index];
+ memif_region_t *mr = &cc->conn->regions[region_index];
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
- memif_msg_queue_elt_t *e =
- (memif_msg_queue_elt_t *) lm->alloc (sizeof (memif_msg_queue_elt_t));
if (e == NULL)
- return memif_syscall_error_handler (errno);
+ return MEMIF_ERR_NOMEM;
- memset (&e->msg, 0, sizeof (e->msg));
memif_msg_add_region_t *ar = &e->msg.add_region;
e->msg.type = MEMIF_MSG_TYPE_ADD_REGION;
@@ -179,34 +180,19 @@ memif_msg_enq_add_region (memif_connection_t * c, uint8_t region_index)
ar->index = region_index;
ar->size = mr->region_size;
- e->next = NULL;
- if (c->msg_queue == NULL)
- {
- c->msg_queue = e;
- return MEMIF_ERR_SUCCESS; /* 0 */
- }
-
- memif_msg_queue_elt_t *cur = c->msg_queue;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = e;
-
return MEMIF_ERR_SUCCESS; /* 0 */
}
/* send information about ring specified by direction (S2M | M2S) and index */
-static_fn int
-memif_msg_enq_add_ring (memif_connection_t * c, uint8_t index, uint8_t dir)
+static int
+memif_msg_enq_add_ring (memif_control_channel_t *cc, uint8_t index,
+ uint8_t dir)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_msg_queue_elt_t *e =
- (memif_msg_queue_elt_t *) lm->alloc (sizeof (memif_msg_queue_elt_t));
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
+
if (e == NULL)
- return memif_syscall_error_handler (errno);
+ return MEMIF_ERR_NOMEM;
- memset (&e->msg, 0, sizeof (e->msg));
memif_msg_add_ring_t *ar = &e->msg.add_ring;
e->msg.type = MEMIF_MSG_TYPE_ADD_RING;
@@ -214,9 +200,9 @@ memif_msg_enq_add_ring (memif_connection_t * c, uint8_t index, uint8_t dir)
/* TODO: support multiple rings */
memif_queue_t *mq;
if (dir == MEMIF_RING_M2S)
- mq = &c->rx_queues[index];
+ mq = &cc->conn->rx_queues[index];
else
- mq = &c->tx_queues[index];
+ mq = &cc->conn->tx_queues[index];
e->fd = mq->int_fd;
ar->index = index;
@@ -226,119 +212,81 @@ memif_msg_enq_add_ring (memif_connection_t * c, uint8_t index, uint8_t dir)
ar->flags = (dir == MEMIF_RING_S2M) ? MEMIF_MSG_ADD_RING_FLAG_S2M : 0;
ar->private_hdr_size = 0;
- e->next = NULL;
- if (c->msg_queue == NULL)
- {
- c->msg_queue = e;
- return MEMIF_ERR_SUCCESS; /* 0 */
- }
-
- memif_msg_queue_elt_t *cur = c->msg_queue;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = e;
-
return MEMIF_ERR_SUCCESS; /* 0 */
}
/* used as connection request from slave */
-static_fn int
-memif_msg_enq_connect (memif_connection_t * c)
+static int
+memif_msg_enq_connect (memif_control_channel_t *cc)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_msg_queue_elt_t *e =
- (memif_msg_queue_elt_t *) lm->alloc (sizeof (memif_msg_queue_elt_t));
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
+
if (e == NULL)
- return memif_syscall_error_handler (errno);
+ return MEMIF_ERR_NOMEM;
- memset (&e->msg, 0, sizeof (e->msg));
memif_msg_connect_t *cm = &e->msg.connect;
e->msg.type = MEMIF_MSG_TYPE_CONNECT;
e->fd = -1;
- strlcpy ((char *) cm->if_name, (char *) c->args.interface_name,
+ strlcpy ((char *) cm->if_name, (char *) cc->conn->args.interface_name,
sizeof (cm->if_name));
- e->next = NULL;
- if (c->msg_queue == NULL)
- {
- c->msg_queue = e;
- return MEMIF_ERR_SUCCESS; /* 0 */
- }
-
- memif_msg_queue_elt_t *cur = c->msg_queue;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = e;
-
return MEMIF_ERR_SUCCESS; /* 0 */
}
/* used as confirmation of connection by master */
-static_fn int
-memif_msg_enq_connected (memif_connection_t * c)
+static int
+memif_msg_enq_connected (memif_control_channel_t *cc)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_msg_queue_elt_t *e =
- (memif_msg_queue_elt_t *) lm->alloc (sizeof (memif_msg_queue_elt_t));
+ memif_msg_queue_elt_t *e = memif_msg_enq (cc);
+
if (e == NULL)
- return memif_syscall_error_handler (errno);
+ return MEMIF_ERR_NOMEM;
- memset (&e->msg, 0, sizeof (e->msg));
memif_msg_connected_t *cm = &e->msg.connected;
e->msg.type = MEMIF_MSG_TYPE_CONNECTED;
e->fd = -1;
- strlcpy ((char *) cm->if_name, (char *) c->args.interface_name,
+ strlcpy ((char *) cm->if_name, (char *) cc->conn->args.interface_name,
sizeof (cm->if_name));
- e->next = NULL;
- if (c->msg_queue == NULL)
- {
- c->msg_queue = e;
- return MEMIF_ERR_SUCCESS; /* 0 */
- }
-
- memif_msg_queue_elt_t *cur = c->msg_queue;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = e;
-
return MEMIF_ERR_SUCCESS; /* 0 */
}
-/* immediately send disconnect msg */
- /* specify protocol for disconnect msg err_code
- so that it will be compatible with VPP? (header/doc) */
int
-memif_msg_send_disconnect (int fd, uint8_t * err_string, uint32_t err_code)
+memif_msg_enq_disconnect (memif_control_channel_t *cc, uint8_t *err_string,
+ uint32_t err_code)
{
- memif_msg_t msg = { 0 };
- memif_msg_disconnect_t *d = &msg.disconnect;
+ memif_msg_queue_elt_t *e;
+
+ e = cc->sock->args.alloc (sizeof (*e));
+ if (e == NULL)
+ return MEMIF_ERR_NOMEM;
- msg.type = MEMIF_MSG_TYPE_DISCONNECT;
+ e->fd = -1;
+ /* Insert disconenct message at the top of the msg queue */
+ TAILQ_INSERT_HEAD (&cc->msg_queue, e, next);
+
+ memif_msg_disconnect_t *d = &e->msg.disconnect;
+
+ e->msg.type = MEMIF_MSG_TYPE_DISCONNECT;
d->code = err_code;
- uint16_t l = strlen ((char *) err_string);
- if (l > sizeof (d->string) - 1)
+ uint16_t l = sizeof (d->string);
+ if (l > 96)
{
- DBG ("Disconnect string too long. Sending the first %d characters.",
+ DBG ("Disconnect string too long. Sending the first %ld characters.",
sizeof (d->string) - 1);
}
strlcpy ((char *) d->string, (char *) err_string, sizeof (d->string));
- return memif_msg_send (fd, &msg, -1);
+ return MEMIF_ERR_SUCCESS;
}
-static_fn int
-memif_msg_receive_hello (memif_connection_t * c, memif_msg_t * msg)
+static int
+memif_msg_parse_hello (memif_control_channel_t *cc, memif_msg_t *msg)
{
memif_msg_hello_t *h = &msg->hello;
+ memif_connection_t *c = cc->conn;
if (msg->hello.min_version > MEMIF_VERSION ||
msg->hello.max_version < MEMIF_VERSION)
@@ -360,139 +308,73 @@ memif_msg_receive_hello (memif_connection_t * c, memif_msg_t * msg)
}
/* handle interface identification (id, secret (optional)) */
-static_fn int
-memif_msg_receive_init (memif_socket_t * ms, int fd, memif_msg_t * msg)
+static int
+memif_msg_parse_init (memif_control_channel_t *cc, memif_msg_t *msg)
{
memif_msg_init_t *i = &msg->init;
- memif_list_elt_t *elt = NULL;
- memif_list_elt_t elt2;
memif_connection_t *c = NULL;
- libmemif_main_t *lm = get_libmemif_main (ms);
uint8_t err_string[96];
memset (err_string, 0, sizeof (char) * 96);
int err = MEMIF_ERR_SUCCESS; /* 0 */
+ /* Check compatible meimf version */
if (i->version != MEMIF_VERSION)
{
DBG ("MEMIF_VER_ERR");
- strncpy ((char *) err_string, MEMIF_VER_ERR, strlen (MEMIF_VER_ERR));
- err = MEMIF_ERR_PROTO;
- goto error;
- }
-
- get_list_elt (&elt, ms->interface_list, ms->interface_list_len, i->id);
- if (elt == NULL)
- {
- DBG ("MEMIF_ID_ERR");
- strncpy ((char *) err_string, MEMIF_ID_ERR, strlen (MEMIF_ID_ERR));
- err = MEMIF_ERR_ID;
- goto error;
- }
-
- c = (memif_connection_t *) elt->data_struct;
-
- if (!(c->args.is_master))
- {
- DBG ("MEMIF_SLAVE_ERR");
- strncpy ((char *) err_string, MEMIF_SLAVE_ERR,
- strlen (MEMIF_SLAVE_ERR));
- err = MEMIF_ERR_ACCSLAVE;
- goto error;
- }
- if (c->fd != -1)
- {
- DBG ("MEMIF_CONN_ERR");
- strncpy ((char *) err_string, MEMIF_CONN_ERR, strlen (MEMIF_CONN_ERR));
- err = MEMIF_ERR_ALRCONN;
- goto error;
- }
-
- c->fd = fd;
-
- if (i->mode != c->args.mode)
- {
- DBG ("MEMIF_MODE_ERR");
- strncpy ((char *) err_string, MEMIF_MODE_ERR, strlen (MEMIF_MODE_ERR));
- err = MEMIF_ERR_MODE;
- goto error;
- }
-
- strlcpy ((char *) c->remote_name, (char *) i->name, sizeof (c->remote_name));
-
- if (strlen ((char *) c->args.secret) > 0)
- {
- int r;
- if (strlen ((char *) i->secret) > 0)
- {
- if (strlen ((char *) c->args.secret) != strlen ((char *) i->secret))
- {
- DBG ("MEMIF_SECRET_ERR");
- strncpy ((char *) err_string,
- MEMIF_SECRET_ERR, strlen (MEMIF_SECRET_ERR));
- err = MEMIF_ERR_SECRET;
- goto error;
- }
- r = strncmp ((char *) i->secret, (char *) c->args.secret,
- strlen ((char *) c->args.secret));
- if (r != 0)
- {
- DBG ("MEMIF_SECRET_ERR");
- strncpy ((char *) err_string,
- MEMIF_SECRET_ERR, strlen (MEMIF_SECRET_ERR));
- err = MEMIF_ERR_SECRET;
- goto error;
- }
- }
- else
- {
- DBG ("MEMIF_NOSECRET_ERR");
- strncpy ((char *) err_string,
- MEMIF_NOSECRET_ERR, strlen (MEMIF_NOSECRET_ERR));
- err = MEMIF_ERR_NOSECRET;
- goto error;
- }
+ memif_msg_enq_disconnect (cc, MEMIF_VER_ERR, 0);
+ return MEMIF_ERR_PROTO;
}
- c->read_fn = memif_conn_fd_read_ready;
- c->write_fn = memif_conn_fd_write_ready;
- c->error_fn = memif_conn_fd_error;
+ /* Find endpoint on the socket */
+ TAILQ_FOREACH (c, &cc->sock->master_interfaces, next)
+ {
+ /* Match interface id */
+ if (c->args.interface_id != i->id)
+ continue;
+ /* If control channel is present, interface is connected (or connecting) */
+ if (c->control_channel != NULL)
+ {
+ memif_msg_enq_disconnect (cc, "Already connected", 0);
+ return MEMIF_ERR_ALRCONN;
+ }
+ /* Verify secret */
+ if (c->args.secret[0] != '\0')
+ {
+ if (strncmp ((char *) c->args.secret, (char *) i->secret, 24) != 0)
+ {
+ memif_msg_enq_disconnect (cc, "Incorrect secret", 0);
+ return MEMIF_ERR_SECRET;
+ }
+ }
+
+ /* Assign the control channel to this interface */
+ c->control_channel = cc;
+ cc->conn = c;
+
+ strlcpy ((char *) c->remote_name, (char *) i->name,
+ sizeof (c->remote_name));
+ }
- elt2.key = c->fd;
- elt2.data_struct = c;
-
- add_list_elt (lm, &elt2, &lm->control_list, &lm->control_list_len);
- free_list_elt (lm->pending_list, lm->pending_list_len, fd);
-
- return err;
-
-error:
- memif_msg_send_disconnect (fd, err_string, 0);
- lm->control_fd_update (fd, MEMIF_FD_EVENT_DEL, lm->private_ctx);
- free_list_elt (lm->pending_list, lm->pending_list_len, fd);
- close (fd);
- fd = -1;
return err;
}
/* receive region information and add new region to connection (if possible) */
-static_fn int
-memif_msg_receive_add_region (memif_connection_t * c, memif_msg_t * msg,
- int fd)
+static int
+memif_msg_parse_add_region (memif_control_channel_t *cc, memif_msg_t *msg,
+ int fd)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
-
memif_msg_add_region_t *ar = &msg->add_region;
memif_region_t *mr;
+ memif_connection_t *c = cc->conn;
+
if (fd < 0)
return MEMIF_ERR_NO_SHMFD;
if (ar->index > MEMIF_MAX_REGION)
return MEMIF_ERR_MAXREG;
- mr =
- (memif_region_t *) lm->realloc (c->regions,
- sizeof (memif_region_t) *
- (++c->regions_num));
+ mr = (memif_region_t *) cc->sock->args.realloc (
+ c->regions, sizeof (memif_region_t) * (++c->regions_num));
if (mr == NULL)
return memif_syscall_error_handler (errno);
memset (mr + ar->index, 0, sizeof (memif_region_t));
@@ -500,9 +382,8 @@ memif_msg_receive_add_region (memif_connection_t * c, memif_msg_t * msg,
c->regions[ar->index].fd = fd;
c->regions[ar->index].region_size = ar->size;
c->regions[ar->index].addr = NULL;
-
/* region 0 is never external */
- if (lm->get_external_region_addr && (ar->index != 0))
+ if (cc->sock->get_external_region_addr && (ar->index != 0))
c->regions[ar->index].is_external = 1;
return MEMIF_ERR_SUCCESS; /* 0 */
@@ -510,12 +391,12 @@ memif_msg_receive_add_region (memif_connection_t * c, memif_msg_t * msg,
/* receive ring information and add new ring to connection queue
(based on direction S2M | M2S) */
-static_fn int
-memif_msg_receive_add_ring (memif_connection_t * c, memif_msg_t * msg, int fd)
+static int
+memif_msg_parse_add_ring (memif_control_channel_t *cc, memif_msg_t *msg,
+ int fd)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
-
memif_msg_add_ring_t *ar = &msg->add_ring;
+ memif_connection_t *c = cc->conn;
memif_queue_t *mq;
@@ -532,10 +413,8 @@ memif_msg_receive_add_ring (memif_connection_t * c, memif_msg_t * msg, int fd)
if (ar->index >= c->args.num_s2m_rings)
return MEMIF_ERR_MAXRING;
- mq =
- (memif_queue_t *) lm->realloc (c->rx_queues,
- sizeof (memif_queue_t) *
- (++c->rx_queues_num));
+ mq = (memif_queue_t *) cc->sock->args.realloc (
+ c->rx_queues, sizeof (memif_queue_t) * (++c->rx_queues_num));
memset (mq + ar->index, 0, sizeof (memif_queue_t));
if (mq == NULL)
return memif_syscall_error_handler (errno);
@@ -553,10 +432,8 @@ memif_msg_receive_add_ring (memif_connection_t * c, memif_msg_t * msg, int fd)
if (ar->index >= c->args.num_m2s_rings)
return MEMIF_ERR_MAXRING;
- mq =
- (memif_queue_t *) lm->realloc (c->tx_queues,
- sizeof (memif_queue_t) *
- (++c->tx_queues_num));
+ mq = (memif_queue_t *) cc->sock->args.realloc (
+ c->tx_queues, sizeof (memif_queue_t) * (++c->tx_queues_num));
memset (mq + ar->index, 0, sizeof (memif_queue_t));
if (mq == NULL)
return memif_syscall_error_handler (errno);
@@ -571,15 +448,65 @@ memif_msg_receive_add_ring (memif_connection_t * c, memif_msg_t * msg, int fd)
return MEMIF_ERR_SUCCESS; /* 0 */
}
+static int
+memif_configure_rx_interrupt (memif_connection_t *c)
+{
+ memif_socket_t *ms = (memif_socket_t *) c->args.socket;
+ memif_interrupt_t *idata;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ void *ctx;
+ int i;
+
+ if (c->on_interrupt != NULL)
+ {
+ for (i = 0; i < c->run_args.num_m2s_rings; i++)
+ {
+ /* Allocate fd event data */
+ fdata = ms->args.alloc (sizeof (*fdata));
+ if (fdata == NULL)
+ {
+ memif_msg_enq_disconnect (c->control_channel, "Internal error",
+ 0);
+ return MEMIF_ERR_NOMEM;
+ }
+ /* Allocate interrupt data */
+ idata = ms->args.alloc (sizeof (*fdata));
+ if (idata == NULL)
+ {
+ ms->args.free (fdata);
+ memif_msg_enq_disconnect (c->control_channel, "Internal error",
+ 0);
+ return MEMIF_ERR_NOMEM;
+ }
+
+ /* configure interrupt data */
+ idata->c = c;
+ idata->qid = i;
+ /* configure fd event data */
+ fdata->event_handler = memif_interrupt_handler;
+ fdata->private_ctx = idata;
+ fde.fd = c->rx_queues[i].int_fd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
+
+ /* Start listening for events */
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
+ }
+ }
+
+ return MEMIF_ERR_SUCCESS;
+}
+
/* slave -> master */
-static_fn int
-memif_msg_receive_connect (memif_connection_t * c, memif_msg_t * msg)
+static int
+memif_msg_parse_connect (memif_control_channel_t *cc, memif_msg_t *msg)
{
memif_msg_connect_t *cm = &msg->connect;
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- memif_list_elt_t elt;
-
+ memif_connection_t *c = cc->conn;
int err;
+
err = memif_connect1 (c);
if (err != MEMIF_ERR_SUCCESS)
return err;
@@ -587,21 +514,9 @@ memif_msg_receive_connect (memif_connection_t * c, memif_msg_t * msg)
strlcpy ((char *) c->remote_if_name, (char *) cm->if_name,
sizeof (c->remote_if_name));
- int i;
- if (c->on_interrupt != NULL)
- {
- for (i = 0; i < c->run_args.num_m2s_rings; i++)
- {
- elt.key = c->rx_queues[i].int_fd;
- elt.data_struct = c;
- add_list_elt (lm, &elt, &lm->interrupt_list,
- &lm->interrupt_list_len);
-
- lm->control_fd_update (c->rx_queues[i].int_fd, MEMIF_FD_EVENT_READ,
- lm->private_ctx);
- }
-
- }
+ err = memif_configure_rx_interrupt (c);
+ if (err != MEMIF_ERR_SUCCESS)
+ return err;
c->on_connect ((void *) c, c->private_ctx);
@@ -609,43 +524,38 @@ memif_msg_receive_connect (memif_connection_t * c, memif_msg_t * msg)
}
/* master -> slave */
-static_fn int
-memif_msg_receive_connected (memif_connection_t * c, memif_msg_t * msg)
+static int
+memif_msg_parse_connected (memif_control_channel_t *cc, memif_msg_t *msg)
{
memif_msg_connect_t *cm = &msg->connect;
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
+ memif_connection_t *c = cc->conn;
int err;
err = memif_connect1 (c);
if (err != MEMIF_ERR_SUCCESS)
return err;
- strncpy ((char *) c->remote_if_name, (char *) cm->if_name,
+ strlcpy ((char *) c->remote_if_name, (char *) cm->if_name,
sizeof (c->remote_if_name));
- int i;
- if (c->on_interrupt != NULL)
- {
- for (i = 0; i < c->run_args.num_s2m_rings; i++)
- {
- lm->control_fd_update (c->rx_queues[i].int_fd, MEMIF_FD_EVENT_READ,
- lm->private_ctx);
- }
- }
+ err = memif_configure_rx_interrupt (c);
+ if (err != MEMIF_ERR_SUCCESS)
+ return err;
c->on_connect ((void *) c, c->private_ctx);
return err;
}
-static_fn int
-memif_msg_receive_disconnect (memif_connection_t * c, memif_msg_t * msg)
+static int
+memif_msg_parse_disconnect (memif_control_channel_t *cc, memif_msg_t *msg)
{
memif_msg_disconnect_t *d = &msg->disconnect;
+ memif_connection_t *c = cc->conn;
memset (c->remote_disconnect_string, 0,
sizeof (c->remote_disconnect_string));
- strncpy ((char *) c->remote_disconnect_string, (char *) d->string,
+ strlcpy ((char *) c->remote_disconnect_string, (char *) d->string,
sizeof (c->remote_disconnect_string));
/* on returning error, handle function will call memif_disconnect () */
@@ -654,8 +564,8 @@ memif_msg_receive_disconnect (memif_connection_t * c, memif_msg_t * msg)
return MEMIF_ERR_DISCONNECT;
}
-static_fn int
-memif_msg_receive (libmemif_main_t * lm, int ifd)
+static int
+memif_msg_receive_and_parse (memif_control_channel_t *cc)
{
char ctl[CMSG_SPACE (sizeof (int)) +
CMSG_SPACE (sizeof (struct ucred))] = { 0 };
@@ -666,9 +576,7 @@ memif_msg_receive (libmemif_main_t * lm, int ifd)
int err = MEMIF_ERR_SUCCESS; /* 0 */
int fd = -1;
int i;
- memif_connection_t *c = NULL;
memif_socket_t *ms = NULL;
- memif_list_elt_t *elt = NULL;
iov[0].iov_base = (void *) &msg;
iov[0].iov_len = sizeof (memif_msg_t);
@@ -677,8 +585,8 @@ memif_msg_receive (libmemif_main_t * lm, int ifd)
mh.msg_control = ctl;
mh.msg_controllen = sizeof (ctl);
- DBG ("recvmsg fd %d", ifd);
- size = recvmsg (ifd, &mh, 0);
+ DBG ("recvmsg fd %d", cc->fd);
+ size = recvmsg (cc->fd, &mh, 0);
if (size != sizeof (memif_msg_t))
{
if (size == 0)
@@ -709,93 +617,79 @@ memif_msg_receive (libmemif_main_t * lm, int ifd)
DBG ("Message type %u received", msg.type);
- get_list_elt (&elt, lm->control_list, lm->control_list_len, ifd);
- if (elt != NULL)
- c = (memif_connection_t *) elt->data_struct;
-
switch (msg.type)
{
case MEMIF_MSG_TYPE_ACK:
break;
case MEMIF_MSG_TYPE_HELLO:
- if ((err = memif_msg_receive_hello (c, &msg)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_hello (cc, &msg)) != MEMIF_ERR_SUCCESS)
return err;
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_init_regions_and_queues (cc->conn)) !=
+ MEMIF_ERR_SUCCESS)
return err;
- if ((err = memif_msg_enq_init (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_init (cc)) != MEMIF_ERR_SUCCESS)
return err;
- for (i = 0; i < c->regions_num; i++)
+ for (i = 0; i < cc->conn->regions_num; i++)
{
- if ((err = memif_msg_enq_add_region (c, i)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_add_region (cc, i)) != MEMIF_ERR_SUCCESS)
return err;
}
- for (i = 0; i < c->run_args.num_s2m_rings; i++)
+ for (i = 0; i < cc->conn->run_args.num_s2m_rings; i++)
{
- if ((err =
- memif_msg_enq_add_ring (c, i,
- MEMIF_RING_S2M)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_add_ring (cc, i, MEMIF_RING_S2M)) !=
+ MEMIF_ERR_SUCCESS)
return err;
}
- for (i = 0; i < c->run_args.num_m2s_rings; i++)
+ for (i = 0; i < cc->conn->run_args.num_m2s_rings; i++)
{
- if ((err =
- memif_msg_enq_add_ring (c, i,
- MEMIF_RING_M2S)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_add_ring (cc, i, MEMIF_RING_M2S)) !=
+ MEMIF_ERR_SUCCESS)
return err;
}
- if ((err = memif_msg_enq_connect (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_connect (cc)) != MEMIF_ERR_SUCCESS)
return err;
break;
case MEMIF_MSG_TYPE_INIT:
- get_list_elt (&elt, lm->pending_list, lm->pending_list_len, ifd);
- if (elt == NULL)
- return -1;
- ms = (memif_socket_t *) elt->data_struct;
- if ((err = memif_msg_receive_init (ms, ifd, &msg)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_init (cc, &msg)) != MEMIF_ERR_SUCCESS)
return err;
/* c->remote_pid = cr->pid */
/* c->remote_uid = cr->uid */
/* c->remote_gid = cr->gid */
- get_list_elt (&elt, lm->control_list, lm->control_list_len, ifd);
- if (elt == NULL)
- return -1;
- c = (memif_connection_t *) elt->data_struct;
- if ((err = memif_msg_enq_ack (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_ack (cc)) != MEMIF_ERR_SUCCESS)
return err;
break;
case MEMIF_MSG_TYPE_ADD_REGION:
- if ((err =
- memif_msg_receive_add_region (c, &msg, fd)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_add_region (cc, &msg, fd)) !=
+ MEMIF_ERR_SUCCESS)
return err;
- if ((err = memif_msg_enq_ack (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_ack (cc)) != MEMIF_ERR_SUCCESS)
return err;
break;
case MEMIF_MSG_TYPE_ADD_RING:
- if ((err =
- memif_msg_receive_add_ring (c, &msg, fd)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_add_ring (cc, &msg, fd)) != MEMIF_ERR_SUCCESS)
return err;
- if ((err = memif_msg_enq_ack (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_ack (cc)) != MEMIF_ERR_SUCCESS)
return err;
break;
case MEMIF_MSG_TYPE_CONNECT:
- if ((err = memif_msg_receive_connect (c, &msg)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_connect (cc, &msg)) != MEMIF_ERR_SUCCESS)
return err;
- if ((err = memif_msg_enq_connected (c)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_enq_connected (cc)) != MEMIF_ERR_SUCCESS)
return err;
break;
case MEMIF_MSG_TYPE_CONNECTED:
- if ((err = memif_msg_receive_connected (c, &msg)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_connected (cc, &msg)) != MEMIF_ERR_SUCCESS)
return err;
break;
case MEMIF_MSG_TYPE_DISCONNECT:
- if ((err = memif_msg_receive_disconnect (c, &msg)) != MEMIF_ERR_SUCCESS)
+ if ((err = memif_msg_parse_disconnect (cc, &msg)) != MEMIF_ERR_SUCCESS)
return err;
break;
@@ -804,101 +698,155 @@ memif_msg_receive (libmemif_main_t * lm, int ifd)
break;
}
- if (c != NULL)
- c->flags |= MEMIF_CONNECTION_FLAG_WRITE;
-
return MEMIF_ERR_SUCCESS; /* 0 */
}
-int
-memif_conn_fd_error (memif_connection_t * c)
+void
+memif_delete_control_channel (memif_control_channel_t *cc)
{
- DBG ("connection fd error");
- strncpy ((char *) c->remote_disconnect_string, "connection fd error", 19);
- int err = memif_disconnect_internal (c);
- return err;
-}
+ memif_msg_queue_elt_t *e, *next;
+ memif_socket_t *ms = cc->sock;
+ memif_fd_event_t fde;
+ void *ctx;
-/* calls memif_msg_receive to handle pending messages on socket */
-int
-memif_conn_fd_read_ready (memif_connection_t * c)
-{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- int err;
+ fde.fd = cc->fd;
+ fde.type = MEMIF_FD_EVENT_DEL;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ cc->sock->args.on_control_fd_update (fde, ctx);
- err = memif_msg_receive (lm, c->fd);
- if (err != 0)
+ if (cc->fd > 0)
+ close (cc->fd);
+
+ /* Clear control message queue */
+ for (e = TAILQ_FIRST (&cc->msg_queue); e != NULL; e = next)
{
- err = memif_disconnect_internal (c);
+ next = TAILQ_NEXT (e, next);
+ TAILQ_REMOVE (&cc->msg_queue, e, next);
+ cc->sock->args.free (e);
}
- return err;
+
+ /* remove reference */
+ if (cc->conn != NULL)
+ cc->conn->control_channel = NULL;
+ cc->conn = NULL;
+ cc->sock->args.free (cc);
+
+ return;
}
-/* get msg from msg queue buffer and send it to socket */
int
-memif_conn_fd_write_ready (memif_connection_t * c)
+memif_control_channel_handler (memif_fd_event_type_t type, void *private_ctx)
{
- libmemif_main_t *lm = get_libmemif_main (c->args.socket);
- int err = MEMIF_ERR_SUCCESS; /* 0 */
-
-
- if ((c->flags & MEMIF_CONNECTION_FLAG_WRITE) == 0)
- goto done;
+ memif_control_channel_t *cc = (memif_control_channel_t *) private_ctx;
+ int err;
- memif_msg_queue_elt_t *e = c->msg_queue;
- if (e == NULL)
- goto done;
+ /* Receive the message, parse the message and
+ * enqueue next message(s).
+ */
+ err = memif_msg_receive_and_parse (cc);
+ /* Can't assign to endpoint */
+ if (cc->conn == NULL)
+ {
+ /* A disconnect message is already in the queue */
+ memif_msg_send_from_queue (cc);
+ memif_delete_control_channel (cc);
- c->msg_queue = c->msg_queue->next;
+ return MEMIF_ERR_SUCCESS;
+ }
+ /* error in memif_msg_receive */
+ if (err != MEMIF_ERR_SUCCESS)
+ goto disconnect;
- c->flags &= ~MEMIF_CONNECTION_FLAG_WRITE;
+ /* Continue connecting, send next message from the queue */
+ err = memif_msg_send_from_queue (cc);
+ if (err != MEMIF_ERR_SUCCESS)
+ goto disconnect;
- err = memif_msg_send (c->fd, &e->msg, e->fd);
- lm->free (e);
- goto done;
+ return MEMIF_ERR_SUCCESS;
-done:
- return err;
+disconnect:
+ memif_disconnect_internal (cc->conn);
+ return MEMIF_ERR_SUCCESS;
}
int
-memif_conn_fd_accept_ready (memif_socket_t * ms)
+memif_listener_handler (memif_fd_event_type_t type, void *private_ctx)
{
- int addr_len;
- struct sockaddr_un client;
- int conn_fd;
- libmemif_main_t *lm = get_libmemif_main (ms);
+ memif_socket_t *ms = (memif_socket_t *) private_ctx;
+ memif_control_channel_t *cc;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ struct sockaddr_un un;
+ int err, sockfd, addr_len = sizeof (un);
+ void *ctx;
+
+ if (ms == NULL)
+ return MEMIF_ERR_INVAL_ARG;
+
+ if (type & MEMIF_FD_EVENT_READ)
+ {
+ /* Accept connection to the listener socket */
+ sockfd = accept (ms->listener_fd, (struct sockaddr *) &un,
+ (socklen_t *) &addr_len);
+ if (sockfd < 0)
+ {
+ return memif_syscall_error_handler (errno);
+ }
- DBG ("accept called");
+ /* Create new control channel */
+ cc = ms->args.alloc (sizeof (*cc));
+ if (cc == NULL)
+ {
+ err = MEMIF_ERR_NOMEM;
+ goto error;
+ }
- addr_len = sizeof (client);
- conn_fd =
- accept (ms->fd, (struct sockaddr *) &client, (socklen_t *) & addr_len);
+ cc->fd = sockfd;
+ /* The connection will be assigned after parsing MEMIF_MSG_TYPE_INIT msg
+ */
+ cc->conn = NULL;
+ cc->sock = ms;
+ TAILQ_INIT (&cc->msg_queue);
- if (conn_fd < 0)
- {
- return memif_syscall_error_handler (errno);
- }
- DBG ("accept fd %d", ms->fd);
- DBG ("conn fd %d", conn_fd);
+ /* Create memif fd event */
+ fdata = ms->args.alloc (sizeof (*fdata));
+ if (fdata == NULL)
+ {
+ err = MEMIF_ERR_NOMEM;
+ goto error;
+ }
- memif_list_elt_t elt;
- elt.key = conn_fd;
- elt.data_struct = ms;
+ fdata->event_handler = memif_control_channel_handler;
+ fdata->private_ctx = cc;
- add_list_elt (lm, &elt, &lm->pending_list, &lm->pending_list_len);
- lm->control_fd_update (conn_fd, MEMIF_FD_EVENT_READ | MEMIF_FD_EVENT_WRITE,
- lm->private_ctx);
+ fde.fd = sockfd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
- return memif_msg_send_hello (lm, conn_fd);
-}
+ /* Start listenning for events on the new control channel */
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
-int
-memif_read_ready (libmemif_main_t * lm, int fd)
-{
- int err;
+ /* enqueue HELLO msg */
+ err = memif_msg_enq_hello (cc);
+ if (err != MEMIF_ERR_SUCCESS)
+ goto error;
+
+ /* send HELLO msg */
+ err = memif_msg_send_from_queue (cc);
+ if (err != MEMIF_ERR_SUCCESS)
+ goto error;
+ }
+
+ return MEMIF_ERR_SUCCESS;
- err = memif_msg_receive (lm, fd);
+error:
+ if (sockfd > 0)
+ close (sockfd);
+ if (cc != NULL)
+ ms->args.free (cc);
+ if (fdata != NULL)
+ ms->args.free (fdata);
return err;
}
diff --git a/extras/libmemif/src/socket.h b/extras/libmemif/src/socket.h
index ea6979d6bd5..0c1a848a677 100644
--- a/extras/libmemif/src/socket.h
+++ b/extras/libmemif/src/socket.h
@@ -31,59 +31,11 @@
/* socket.c */
-int memif_conn_fd_read_ready (memif_connection_t * c);
+int memif_listener_handler (memif_fd_event_type_t type, void *private_ctx);
-int memif_conn_fd_write_ready (memif_connection_t * c);
+int memif_control_channel_handler (memif_fd_event_type_t type,
+ void *private_ctx);
-int memif_conn_fd_error (memif_connection_t * c);
-
-int memif_conn_fd_accept_ready (memif_socket_t * ms);
-
-int memif_read_ready (libmemif_main_t *lm, int fd);
-
-int memif_msg_send_disconnect (int fd, uint8_t * err_string,
- uint32_t err_code);
-
-/* when compiling unit tests, compile functions without static keyword
- and declare functions in header file */
-#ifdef MEMIF_UNIT_TEST
-#define static_fn
-
-int memif_msg_send (int fd, memif_msg_t * msg, int afd);
-
-int memif_msg_enq_ack (memif_connection_t * c);
-
-int memif_msg_send_hello (libmemif_main_t *lm, int fd);
-
-int memif_msg_enq_init (memif_connection_t * c);
-
-int memif_msg_enq_add_region (memif_connection_t * c, uint8_t region);
-
-int memif_msg_enq_add_ring (memif_connection_t * c, uint8_t index,
- uint8_t dir);
-
-int memif_msg_receive_hello (memif_connection_t * c, memif_msg_t * msg);
-
-int memif_msg_receive_init (memif_socket_t * ms, int fd, memif_msg_t * msg);
-
-int memif_msg_receive_add_region (memif_connection_t * c, memif_msg_t * msg,
- int fd);
-
-int memif_msg_receive_add_ring (memif_connection_t * c, memif_msg_t * msg,
- int fd);
-
-int memif_msg_enq_connect (memif_connection_t * c);
-
-int memif_msg_enq_connected (memif_connection_t * c);
-
-int memif_msg_receive_connect (memif_connection_t * c, memif_msg_t * msg);
-
-int memif_msg_receive_connected (memif_connection_t * c, memif_msg_t * msg);
-
-int memif_msg_receive_disconnect (memif_connection_t * c, memif_msg_t * msg);
-
-#else
-#define static_fn static
-#endif /* MEMIF_UNIT_TEST */
+void memif_delete_control_channel (memif_control_channel_t *cc);
#endif /* _SOCKET_H_ */
diff --git a/extras/libmemif/test/CMakeLists.txt b/extras/libmemif/test/CMakeLists.txt
index 79754a2f8b7..cdb0b87f958 100644
--- a/extras/libmemif/test/CMakeLists.txt
+++ b/extras/libmemif/test/CMakeLists.txt
@@ -1,35 +1,2 @@
-# Copyright (c) 2017 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.
-
-cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
-
-set(HEADERS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-set(TEST_NAME libmemif-test)
-set(TEST_LIBS m rt)
-
-find_package(Subunit QUIET)
-if (NOT SUBUNIT_LIBRARY)
- set(SUBUNIT_LIBRARY "")
-endif ()
-
-set(SOURCE_FILES
- main_test.c
- socket_test.c
- unit_test.c
-)
-
-add_executable(${TEST_NAME} ${SOURCE_FILES})
-target_include_directories(${TEST_NAME} PRIVATE $<BUILD_INTERFACE:${HEADERS_DIR}>)
-target_link_libraries(${TEST_NAME} memif ${CHECK_LIBRARY} ${SUBUNIT_LIBRARY} ${TEST_LIBS} ${CMAKE_THREAD_LIBS_INIT})
-
-add_test(unit_test unit-test)
+add_subdirectory(suite_socket)
+add_subdirectory(suite_main)
diff --git a/extras/libmemif/test/main_test.c b/extras/libmemif/test/main_test.c
deleted file mode 100644
index 8ad9961157f..00000000000
--- a/extras/libmemif/test/main_test.c
+++ /dev/null
@@ -1,945 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 <unistd.h>
-
-#include <main_test.h>
-
-#include <memif_private.h>
-
-#define SOCKET_FILENAME "/run/vpp/memif.sock"
-
-uint8_t ready_called;
-#define read_call (1 << 0)
-#define write_call (1 << 1)
-#define error_call (1 << 2)
-
-int
-read_fn (memif_connection_t * c)
-{
- ready_called |= read_call;
- return 0;
-}
-
-int
-write_fn (memif_connection_t * c)
-{
- ready_called |= write_call;
- return 0;
-}
-
-int
-error_fn (memif_connection_t * c)
-{
- ready_called |= error_call;
- return 0;
-}
-
-static void
-register_fd_ready_fn (memif_connection_t * c,
- memif_fn * read_fn, memif_fn * write_fn,
- memif_fn * error_fn)
-{
- c->read_fn = read_fn;
- c->write_fn = write_fn;
- c->error_fn = error_fn;
-}
-
-START_TEST (test_init)
-{
- int err;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- libmemif_main_t *lm = &libmemif_main;
-
- ck_assert_ptr_ne (lm, NULL);
- ck_assert_ptr_ne (lm->control_fd_update, NULL);
- ck_assert_int_gt (lm->timerfd, 2);
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-}
-
-END_TEST
-START_TEST (test_init_epoll)
-{
- int err;
-
- if ((err =
- memif_init (NULL, TEST_APP_NAME, NULL, NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- libmemif_main_t *lm = &libmemif_main;
-
- ck_assert_ptr_ne (lm, NULL);
- ck_assert_ptr_ne (lm->control_fd_update, NULL);
- ck_assert_int_gt (lm->timerfd, 2);
- ck_assert_int_gt (lm->epfd, -1);
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-}
-
-END_TEST
-START_TEST (test_create)
-{
- int err;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- ck_assert_ptr_ne (c, NULL);
-
- ck_assert_uint_eq (c->args.interface_id, args.interface_id);
- ck_assert_uint_eq (c->args.is_master, args.is_master);
- ck_assert_uint_eq (c->args.mode, args.mode);
-
- ck_assert_uint_eq (c->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
- ck_assert_uint_eq (c->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
- ck_assert_uint_eq (c->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
- ck_assert_uint_eq (c->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);
-
- ck_assert_ptr_eq (c->msg_queue, NULL);
- ck_assert_ptr_eq (c->regions, NULL);
- ck_assert_ptr_eq (c->tx_queues, NULL);
- ck_assert_ptr_eq (c->rx_queues, NULL);
-
- ck_assert_int_eq (c->fd, -1);
-
- ck_assert_ptr_ne (c->on_connect, NULL);
- ck_assert_ptr_ne (c->on_disconnect, NULL);
- ck_assert_ptr_ne (c->on_interrupt, NULL);
-
- ck_assert_str_eq ((char *)c->args.interface_name, (char *)args.interface_name);
-
- struct itimerspec timer;
- timerfd_gettime (lm->timerfd, &timer);
-
- ck_assert_msg (timer.it_interval.tv_sec == lm->arm.it_interval.tv_sec,
- "timerfd not armed!");
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_create_master)
-{
- int err, rv;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.is_master = 1;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- ck_assert_ptr_ne (c, NULL);
-
- ck_assert_uint_eq (c->args.interface_id, args.interface_id);
- ck_assert_uint_eq (c->args.is_master, args.is_master);
- ck_assert_uint_eq (c->args.mode, args.mode);
-
- ck_assert_uint_eq (c->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
- ck_assert_uint_eq (c->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
- ck_assert_uint_eq (c->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
- ck_assert_uint_eq (c->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);
-
- ck_assert_ptr_eq (c->msg_queue, NULL);
- ck_assert_ptr_eq (c->regions, NULL);
- ck_assert_ptr_eq (c->tx_queues, NULL);
- ck_assert_ptr_eq (c->rx_queues, NULL);
-
- ck_assert_int_eq (c->fd, -1);
-
- ck_assert_ptr_ne (c->on_connect, NULL);
- ck_assert_ptr_ne (c->on_disconnect, NULL);
- ck_assert_ptr_ne (c->on_interrupt, NULL);
-
- ck_assert_str_eq ((char *)c->args.interface_name, (char *)args.interface_name);
-
- struct stat file_stat;
-
- rv = stat (SOCKET_FILENAME, &file_stat);
- ck_assert_int_eq (rv, 0);
-
- ck_assert (S_ISSOCK (file_stat.st_mode));
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_create_mult)
-{
- int err;
- memif_conn_handle_t conn = NULL;
- memif_conn_handle_t conn1 = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- args.interface_id = 1;
-
- if ((err = memif_create (&conn1, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
- memif_connection_t *c1 = (memif_connection_t *) conn1;
-
- ck_assert_ptr_ne (c, NULL);
- ck_assert_ptr_ne (c1, NULL);
-
- ck_assert_uint_eq (c->args.interface_id, 0);
- ck_assert_uint_eq (c->args.is_master, args.is_master);
- ck_assert_uint_eq (c->args.mode, args.mode);
- ck_assert_uint_eq (c1->args.interface_id, 1);
- ck_assert_uint_eq (c1->args.is_master, args.is_master);
- ck_assert_uint_eq (c1->args.mode, args.mode);
-
- ck_assert_uint_eq (c->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
- ck_assert_uint_eq (c->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
- ck_assert_uint_eq (c->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
- ck_assert_uint_eq (c->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);
- ck_assert_uint_eq (c1->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
- ck_assert_uint_eq (c1->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
- ck_assert_uint_eq (c1->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
- ck_assert_uint_eq (c1->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);
-
- ck_assert_ptr_eq (c->msg_queue, NULL);
- ck_assert_ptr_eq (c->regions, NULL);
- ck_assert_ptr_eq (c->tx_queues, NULL);
- ck_assert_ptr_eq (c->rx_queues, NULL);
- ck_assert_ptr_eq (c1->msg_queue, NULL);
- ck_assert_ptr_eq (c1->regions, NULL);
- ck_assert_ptr_eq (c1->tx_queues, NULL);
- ck_assert_ptr_eq (c1->rx_queues, NULL);
-
- ck_assert_int_eq (c->fd, -1);
- ck_assert_int_eq (c1->fd, -1);
-
- ck_assert_ptr_ne (c->on_connect, NULL);
- ck_assert_ptr_ne (c->on_disconnect, NULL);
- ck_assert_ptr_ne (c->on_interrupt, NULL);
- ck_assert_ptr_ne (c1->on_connect, NULL);
- ck_assert_ptr_ne (c1->on_disconnect, NULL);
- ck_assert_ptr_ne (c1->on_interrupt, NULL);
-
- ck_assert_str_eq ((char *)c->args.interface_name, (char *)args.interface_name);
- ck_assert_str_eq ((char *)c1->args.interface_name, (char *)args.interface_name);
-
- struct itimerspec timer;
- timerfd_gettime (lm->timerfd, &timer);
-
- ck_assert_msg (timer.it_interval.tv_sec == lm->arm.it_interval.tv_sec,
- "timerfd not armed!");
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_control_fd_handler)
-{
- int err;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- if ((err =
- memif_control_fd_handler (lm->timerfd,
- MEMIF_FD_EVENT_READ)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_NO_FILE, "err code: %u, err msg: %s", err,
- memif_strerror (err));
-
- register_fd_ready_fn (c, read_fn, write_fn, error_fn);
- c->fd = 69;
- lm->control_list[0].key = c->fd;
- lm->control_list[0].data_struct = c;
-
- if ((err =
- memif_control_fd_handler (c->fd,
- MEMIF_FD_EVENT_READ)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert (ready_called & read_call);
-
- if ((err =
- memif_control_fd_handler (c->fd,
- MEMIF_FD_EVENT_WRITE)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert (ready_called & write_call);
-
- if ((err =
- memif_control_fd_handler (c->fd,
- MEMIF_FD_EVENT_ERROR)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert (ready_called & error_call);
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_buffer_alloc)
-{
- int err, i;
- uint8_t qid;
- uint16_t buf;
- memif_buffer_t *bufs;
- uint16_t max_buf = 10;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- c->fd = 69;
-
- /* test buffer allocation qid 0 (positive) */
-
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
-
- qid = 0;
- if ((err =
- memif_buffer_alloc (conn, qid, bufs, max_buf,
- &buf,
- MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (buf, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);
-
- /* test buffer allocation qid 1 (positive) */
- free (bufs);
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
-
- qid = 1;
- if ((err =
- memif_buffer_alloc (conn, qid, bufs, max_buf,
- &buf,
- MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (buf, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);
-
- /* test buffer allocation qid 2 (negative) */
-
- free (bufs);
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
-
- qid = 2;
- if ((err =
- memif_buffer_alloc (conn, qid, bufs, max_buf,
- &buf,
- MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_QID, "err code: %u, err msg: %s", err,
- memif_strerror (err));
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- free (bufs);
- bufs = NULL;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_tx_burst)
-{
- int err, i;
- uint16_t max_buf = 10, buf, tx;
- uint8_t qid;
- memif_buffer_t *bufs;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- c->fd = 69;
-
- /* test transmit qid 0 (positive) */
-
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
- qid = 0;
- if ((err =
- memif_buffer_alloc (conn, qid, bufs, max_buf,
- &buf,
- MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (buf, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);
-
- if ((err =
- memif_tx_burst (conn, qid, bufs, max_buf, &tx)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (tx, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_ptr_eq (bufs[i].data, NULL);
-
- /* test transmit qid 1 (positive) */
- free (bufs);
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
- qid = 1;
- if ((err =
- memif_buffer_alloc (conn, qid, bufs, max_buf,
- &buf,
- MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (buf, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);
-
- if ((err =
- memif_tx_burst (conn, qid, bufs, max_buf, &tx)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (tx, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_ptr_eq (bufs[i].data, NULL);
-
- /* test transmit qid 2 (negative) */
- free (bufs);
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
- qid = 2;
- if ((err =
- memif_tx_burst (conn, qid, bufs, max_buf, &tx)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_QID, "err code: %u, err msg: %s", err,
- memif_strerror (err));
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
- free (bufs);
- bufs = NULL;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_rx_burst)
-{
- int err, i;
- uint16_t max_buf = 10, rx;
- uint8_t qid;
- memif_buffer_t *bufs;
- memif_queue_t *mq;
- memif_ring_t *ring;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- c->fd = 69;
-
- /* test receive qid 0 (positive) */
- qid = 0;
- mq = &c->rx_queues[qid];
- ring = mq->ring;
- ring->tail += max_buf;
-
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
-
- if ((err =
- memif_rx_burst (conn, qid, bufs, max_buf, &rx)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (rx, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_ptr_ne (bufs[i].data, NULL);
-
- /* test receive qid 1 (positive) */
- qid = 1;
- mq = &c->rx_queues[qid];
- ring = mq->ring;
- ring->tail += max_buf;
-
- free (bufs);
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
-
- if ((err =
- memif_rx_burst (conn, qid, bufs, max_buf, &rx)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (rx, max_buf);
- for (i = 0; i < max_buf; i++)
- ck_assert_ptr_ne (bufs[i].data, NULL);
-
- /* test receive qid 2 (negative) */
- free (bufs);
- bufs = malloc (sizeof (memif_buffer_t) * max_buf);
-
- if ((err =
- memif_rx_burst (conn, qid, bufs, max_buf, &rx)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_QID, "err code: %u, err msg: %s", err,
- memif_strerror (err));
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
- free (bufs);
- bufs = NULL;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_get_details)
-{
- int err, i;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_details_t md;
- memset (&md, 0, sizeof (md));
- ssize_t buflen = 2048;
- char *buf = malloc (buflen);
- memset (buf, 0, buflen);
-
- if ((err = memif_get_details (conn, &md, buf, buflen)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_str_eq ((char *)md.if_name, (char *)c->args.interface_name);
- ck_assert_str_eq ((char *)md.remote_if_name, (char *)c->remote_if_name);
- ck_assert_str_eq ((char *)md.remote_inst_name, (char *)c->remote_name);
- ck_assert_str_eq ((char *)md.secret, (char *)c->args.secret);
-
- ck_assert_uint_eq (md.id, c->args.interface_id);
- ck_assert_uint_ne (md.role, c->args.is_master);
- ck_assert_uint_eq (md.mode, c->args.mode);
- for (i = 0; i < md.rx_queues_num; i++)
- {
- ck_assert_uint_eq (md.rx_queues[i].qid, i);
- ck_assert_uint_eq (md.rx_queues[i].ring_size,
- (1 << c->args.log2_ring_size));
- ck_assert_uint_eq (md.rx_queues[i].buffer_size, c->args.buffer_size);
- }
- for (i = 0; i < md.tx_queues_num; i++)
- {
- ck_assert_uint_eq (md.tx_queues[i].qid, i);
- ck_assert_uint_eq (md.tx_queues[i].ring_size,
- (1 << c->args.log2_ring_size));
- ck_assert_uint_eq (md.tx_queues[i].buffer_size, c->args.buffer_size);
- }
- ck_assert_uint_eq (md.link_up_down, 0);
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_init_regions_and_queues)
-{
- int err;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_ptr_ne (c->regions, NULL);
- ck_assert_ptr_ne (c->tx_queues, NULL);
- ck_assert_ptr_ne (c->rx_queues, NULL);
-
- ck_assert_ptr_ne (c->regions->addr, NULL);
- ck_assert_ptr_ne (c->tx_queues->ring, NULL);
- ck_assert_ptr_ne (c->rx_queues->ring, NULL);
-
- ck_assert_int_ne (c->regions->fd, -1);
- ck_assert_uint_eq (c->tx_queues->ring->cookie, MEMIF_COOKIE);
- ck_assert_uint_eq (c->rx_queues->ring->cookie, MEMIF_COOKIE);
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_connect1)
-{
- int err;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- if ((err = memif_connect1 (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST
-START_TEST (test_disconnect_internal)
-{
- int err;
- ready_called = 0;
- memif_conn_handle_t conn = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
- args.num_s2m_rings = 2;
- args.num_m2s_rings = 2;
-
- libmemif_main_t *lm = &libmemif_main;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_create (&conn, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *c = (memif_connection_t *) conn;
-
- c->run_args.num_s2m_rings = 2;
- c->run_args.num_m2s_rings = 2;
- c->run_args.log2_ring_size = 10;
- c->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- if ((err = memif_disconnect_internal (c)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_int_eq (c->fd, -1);
-
- ck_assert_ptr_eq (c->tx_queues, NULL);
- ck_assert_ptr_eq (c->rx_queues, NULL);
- ck_assert_ptr_eq (c->regions, NULL);
- ck_assert_ptr_eq (c->msg_queue, NULL);
-
- struct itimerspec timer;
- timerfd_gettime (lm->timerfd, &timer);
-
- ck_assert_msg (timer.it_interval.tv_sec == lm->arm.it_interval.tv_sec,
- "timerfd not armed!");
-
- if (lm->timerfd > 0)
- close (lm->timerfd);
- lm->timerfd = -1;
-
- memif_delete (&conn);
- ck_assert_ptr_eq (conn, NULL);
-}
-
-END_TEST Suite *
-main_suite ()
-{
- Suite *s;
-
- TCase *tc_api;
- TCase *tc_internal;
-
- /* create main test suite */
- s = suite_create ("Libmemif main");
-
- /* create api test case */
- tc_api = tcase_create ("Api calls");
- /* add tests to test case */
- tcase_add_test (tc_api, test_init);
- tcase_add_test (tc_api, test_init_epoll);
- tcase_add_test (tc_api, test_create);
- tcase_add_test (tc_api, test_create_master);
- tcase_add_test (tc_api, test_create_mult);
- tcase_add_test (tc_api, test_control_fd_handler);
- tcase_add_test (tc_api, test_buffer_alloc);
- tcase_add_test (tc_api, test_tx_burst);
- tcase_add_test (tc_api, test_rx_burst);
- tcase_add_test (tc_api, test_get_details);
-
- /* create internal test case */
- tc_internal = tcase_create ("Internal");
- /* add tests to test case */
- tcase_add_test (tc_internal, test_init_regions_and_queues);
- tcase_add_test (tc_internal, test_connect1);
- tcase_add_test (tc_internal, test_disconnect_internal);
-
- /* add test cases to test suite */
- suite_add_tcase (s, tc_api);
- suite_add_tcase (s, tc_internal);
-
- /* return main test suite to test runner */
- return s;
-}
diff --git a/extras/libmemif/test/socket_test.c b/extras/libmemif/test/socket_test.c
deleted file mode 100644
index 2d229383c20..00000000000
--- a/extras/libmemif/test/socket_test.c
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 <socket_test.h>
-
-#include <memif_private.h>
-#include <socket.h>
-
-static int
-get_queue_len (memif_msg_queue_elt_t * q)
-{
- int r = 0;
- memif_msg_queue_elt_t *c = q;
- while (c != NULL)
- {
- r++;
- c = c->next;
- }
- return r;
-}
-
-static void
-queue_free (memif_msg_queue_elt_t ** e)
-{
- if (*e == NULL)
- return;
- queue_free (&(*e)->next);
- free (*e);
- *e = NULL;
- return;
-}
-
-START_TEST (test_msg_queue)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t conn;
- conn.msg_queue = NULL;
- conn.fd = -1;
-
-
- int i, len = 10;
-
- for (i = 0; i < len; i++)
- {
- if (i % 2)
- memif_msg_enq_ack (&conn);
- else
- memif_msg_enq_init (&conn);
- }
-
- ck_assert_int_eq (len, get_queue_len (conn.msg_queue));
-
- int pop = 6;
-
- for (i = 0; i < pop; i++)
- {
- if (i % 2)
- {
- ck_assert_uint_eq (conn.msg_queue->msg.type, MEMIF_MSG_TYPE_ACK);
- }
- else
- {
- ck_assert_uint_eq (conn.msg_queue->msg.type, MEMIF_MSG_TYPE_INIT);
- }
- conn.flags |= MEMIF_CONNECTION_FLAG_WRITE;
- /* function will return -1 because no socket is created */
- memif_conn_fd_write_ready (&conn);
- }
-
- ck_assert_int_eq ((len - pop), get_queue_len (conn.msg_queue));
-
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_enq_ack)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- memif_connection_t conn;
- conn.msg_queue = NULL;
-
- if ((err = memif_msg_enq_ack (&conn)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- memif_msg_queue_elt_t *e = conn.msg_queue;
-
- ck_assert_uint_eq (e->msg.type, MEMIF_MSG_TYPE_ACK);
- ck_assert_int_eq (e->fd, -1);
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_enq_init)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- memif_connection_t conn;
- conn.msg_queue = NULL;
-
- conn.args.interface_id = 69;
- conn.args.mode = 0;
-
- strncpy ((char *) conn.args.secret, TEST_SECRET, strlen (TEST_SECRET));
-
- if ((err = memif_msg_enq_init (&conn)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_queue_elt_t *e = conn.msg_queue;
-
- ck_assert_uint_eq (e->msg.type, MEMIF_MSG_TYPE_INIT);
- ck_assert_int_eq (e->fd, -1);
-
- memif_msg_init_t *i = &e->msg.init;
-
- ck_assert_uint_eq (i->version, MEMIF_VERSION);
- ck_assert_uint_eq (i->id, conn.args.interface_id);
- ck_assert_uint_eq (i->mode, conn.args.mode);
- ck_assert_str_eq ((char *)i->secret, (char *)conn.args.secret);
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_enq_add_region)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- memif_connection_t conn;
- conn.msg_queue = NULL;
- conn.regions = (memif_region_t *) malloc (sizeof (memif_region_t));
- memif_region_t *mr = conn.regions;
- mr->fd = 5;
- mr->region_size = 2048;
- uint8_t region_index = 0;
-
- if ((err =
- memif_msg_enq_add_region (&conn, region_index)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_queue_elt_t *e = conn.msg_queue;
-
- ck_assert_uint_eq (e->msg.type, MEMIF_MSG_TYPE_ADD_REGION);
- ck_assert_int_eq (e->fd, mr->fd);
-
- memif_msg_add_region_t *ar = &e->msg.add_region;
-
- ck_assert_uint_eq (ar->index, region_index);
- ck_assert_uint_eq (ar->size, mr->region_size);
-
- free (conn.regions);
- conn.regions = NULL;
- mr = NULL;
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_enq_add_ring)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t conn;
- conn.msg_queue = NULL;
- conn.rx_queues = (memif_queue_t *) malloc (sizeof (memif_queue_t));
- conn.tx_queues = (memif_queue_t *) malloc (sizeof (memif_queue_t));
-
- memif_queue_t *mq = conn.tx_queues;
- uint8_t dir = MEMIF_RING_S2M;
- mq->int_fd = 5;
- mq->offset = 0;
- mq->log2_ring_size = 10;
-
- if ((err = memif_msg_enq_add_ring (&conn, 0, dir)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_queue_elt_t *e = conn.msg_queue;
-
- ck_assert_uint_eq (e->msg.type, MEMIF_MSG_TYPE_ADD_RING);
- ck_assert_int_eq (e->fd, mq->int_fd);
-
- memif_msg_add_ring_t *ar = &e->msg.add_ring;
-
- ck_assert_uint_eq (ar->index, 0);
- ck_assert_uint_eq (ar->offset, mq->offset);
- ck_assert_uint_eq (ar->log2_ring_size, mq->log2_ring_size);
- ck_assert (ar->flags & MEMIF_MSG_ADD_RING_FLAG_S2M);
-
- dir = MEMIF_RING_M2S;
- if ((err = memif_msg_enq_add_ring (&conn, 0, dir)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_enq_connect)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- memif_connection_t conn;
- conn.msg_queue = NULL;
- memset (conn.args.interface_name, 0, sizeof (conn.args.interface_name));
- strncpy ((char *) conn.args.interface_name, TEST_IF_NAME,
- strlen (TEST_IF_NAME));
-
- if ((err = memif_msg_enq_connect (&conn)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_queue_elt_t *e = conn.msg_queue;
-
- ck_assert_uint_eq (e->msg.type, MEMIF_MSG_TYPE_CONNECT);
- ck_assert_int_eq (e->fd, -1);
- ck_assert_str_eq ((char *)e->msg.connect.if_name, TEST_IF_NAME);
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_enq_connected)
-{
- int err;
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
- memif_connection_t conn;
- conn.msg_queue = NULL;
- memset (conn.args.interface_name, 0, sizeof (conn.args.interface_name));
- strncpy ((char *) conn.args.interface_name, TEST_IF_NAME,
- strlen (TEST_IF_NAME));
-
- if ((err = memif_msg_enq_connected (&conn)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_queue_elt_t *e = conn.msg_queue;
-
- ck_assert_uint_eq (e->msg.type, MEMIF_MSG_TYPE_CONNECTED);
- ck_assert_int_eq (e->fd, -1);
- ck_assert_str_eq ((char *)e->msg.connect.if_name, TEST_IF_NAME);
- queue_free (&conn.msg_queue);
-}
-
-END_TEST
-START_TEST (test_send)
-{
- int err;
- int fd = -1, afd = 5;
- memif_msg_t msg;
- memset (&msg, 0, sizeof (msg));
-
- if ((err = memif_msg_send (fd, &msg, afd)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_BAD_FD,
- "err code: %u, err msg: %s", err, memif_strerror (err));
-}
-
-END_TEST
-START_TEST (test_send_hello)
-{
- int err;
- memif_connection_t conn;
- conn.fd = -1;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- conn.args.socket = libmemif_main.default_socket;
-
- if ((err = memif_msg_send_hello (get_libmemif_main (conn.args.socket), conn.fd)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_BAD_FD,
- "err code: %u, err msg: %s", err, memif_strerror (err));
-}
-
-END_TEST
-START_TEST (test_send_disconnect)
-{
- int err;
- memif_connection_t conn;
- conn.fd = -1;
-
- /* only possible fail if memif_msg_send fails... */
- /* obsolete without socket */
- if ((err =
- memif_msg_send_disconnect (conn.fd, (uint8_t *)"unit_test_dc",
- 0)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_BAD_FD, "err code: %u, err msg: %s", err,
- memif_strerror (err));
-}
-
-END_TEST
-START_TEST (test_recv_hello)
-{
- int err;
- memif_connection_t conn;
- memif_msg_t msg;
-
- memif_msg_hello_t *h = &msg.hello;
-
- msg.type = MEMIF_MSG_TYPE_HELLO;
-
- h->min_version = MEMIF_VERSION;
- h->max_version = MEMIF_VERSION;
- h->max_s2m_ring = 1;
- h->max_m2s_ring = 1;
- h->max_log2_ring_size = 14;
- strncpy ((char *) h->name, TEST_IF_NAME, strlen (TEST_IF_NAME));
- memset (conn.remote_name, 0, sizeof (conn.remote_name));
-
- conn.args.num_s2m_rings = 4;
- conn.args.num_m2s_rings = 6;
- conn.args.log2_ring_size = 10;
-
- if ((err = memif_msg_receive_hello (&conn, &msg)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_uint_eq (conn.run_args.num_s2m_rings, 2);
- ck_assert_uint_eq (conn.run_args.num_m2s_rings, 2);
- ck_assert_uint_eq (conn.run_args.log2_ring_size, 10);
- ck_assert_str_eq ((char *)conn.remote_name, TEST_IF_NAME);
-
- h->max_version = 9;
- if ((err = memif_msg_receive_hello (&conn, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_PROTO,
- "err code: %u, err msg: %s", err, memif_strerror (err));
-}
-
-END_TEST
-START_TEST (test_recv_init)
-{
- int err;
- memif_connection_t conn;
-
- conn.args.interface_id = 69;
- conn.args.is_master = 1;
- conn.fd = -1;
- conn.args.mode = 0;
- memset (conn.args.secret, '\0', 24);
- strncpy ((char *) conn.args.secret, TEST_SECRET, strlen (TEST_SECRET));
-
- memif_msg_t msg;
-
- memif_msg_init_t *i = &msg.init;
-
- msg.type = MEMIF_MSG_TYPE_INIT;
-
- i->version = MEMIF_VERSION;
- i->id = 69;
- i->mode = 0;
- memset (i->name, '\0', 32);
- memset (i->secret, '\0', 24);
- strncpy ((char *) i->name, TEST_IF_NAME, strlen (TEST_IF_NAME));
- strncpy ((char *) i->secret, TEST_SECRET, strlen (TEST_SECRET));
-
- memif_socket_t ms;
- ms.interface_list_len = 1;
- ms.interface_list = malloc (sizeof (memif_list_elt_t));
- memif_list_elt_t elt;
- elt.key = 69;
- elt.data_struct = &conn;
- add_list_elt (get_libmemif_main (conn.args.socket), &elt, &ms.interface_list, &ms.interface_list_len);
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- i->version = 9;
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_PROTO,
- "err code: %u, err msg: %s", err, memif_strerror (err));
- i->version = MEMIF_VERSION;
-
- i->id = 78;
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_ID,
- "err code: %u, err msg: %s", err, memif_strerror (err));
- i->id = 69;
-
- i->mode = 1;
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_MODE,
- "err code: %u, err msg: %s", err, memif_strerror (err));
- i->mode = 0;
-
- i->secret[0] = '\0';
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_SECRET,
- "err code: %u, err msg: %s", err, memif_strerror (err));
- strncpy ((char *) i->secret, TEST_SECRET, strlen (TEST_SECRET));
-
- conn.args.is_master = 0;
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_ACCSLAVE,
- "err code: %u, err msg: %s", err, memif_strerror (err));
- conn.args.is_master = 1;
-
- conn.fd = 5;
- if ((err = memif_msg_receive_init (&ms, -1, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg ((err == MEMIF_ERR_ALRCONN) || (err == MEMIF_ERR_BAD_FD),
- "err code: %u, err msg: %s", err, memif_strerror (err));
-}
-
-END_TEST
-START_TEST (test_recv_add_region)
-{
- int err;
- memif_connection_t conn;
- conn.regions = NULL;
- memif_msg_t msg;
- msg.type = MEMIF_MSG_TYPE_ADD_REGION;
- msg.add_region.size = 2048;
- msg.add_region.index = 0;
-
- int fd = 5;
-
- if ((err =
- memif_msg_receive_add_region (&conn, &msg, fd)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_region_t *mr = conn.regions;
-
- ck_assert_uint_eq (mr->fd, fd);
- ck_assert_uint_eq (mr->region_size, 2048);
- ck_assert_ptr_eq (mr->addr, NULL);
-}
-
-END_TEST
-START_TEST (test_recv_add_ring)
-{
- int err;
- memif_connection_t conn;
- int fd = 5;
- memif_msg_t msg;
- conn.args.num_s2m_rings = 2;
- conn.args.num_m2s_rings = 2;
- conn.rx_queues = NULL;
- conn.tx_queues = NULL;
-
- msg.type = MEMIF_MSG_TYPE_ADD_RING;
- memif_msg_add_ring_t *ar = &msg.add_ring;
-
- ar->log2_ring_size = 10;
- ar->region = 0;
- ar->offset = 0;
- ar->flags = 0;
- ar->flags |= MEMIF_MSG_ADD_RING_FLAG_S2M;
- ar->index = 1;
-
- if ((err =
- memif_msg_receive_add_ring (&conn, &msg, fd)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
-
- ar->offset = 2048;
- ar->flags &= ~MEMIF_MSG_ADD_RING_FLAG_S2M;
-
- if ((err =
- memif_msg_receive_add_ring (&conn, &msg, fd)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
-}
-
-END_TEST
-START_TEST (test_recv_connect)
-{
- int err;
- memif_conn_handle_t c = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
-
- args.interface_id = 0;
- args.is_master = 0;
- args.mode = 0;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- if ((err = memif_create (&c, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *conn = (memif_connection_t *) c;
-
- conn->run_args.num_s2m_rings = 1;
- conn->run_args.num_m2s_rings = 1;
- conn->run_args.log2_ring_size = 10;
- conn->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (conn)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_t msg;
- memset (&msg, 0, sizeof (msg));
- msg.type = MEMIF_MSG_TYPE_CONNECT;
-
- memset (msg.connect.if_name, 0, sizeof (msg.connect.if_name));
- strncpy ((char *) msg.connect.if_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_msg_receive_connect (conn, &msg)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_str_eq ((char *)conn->remote_if_name, TEST_IF_NAME);
-}
-
-END_TEST
-START_TEST (test_recv_connected)
-{
- int err;
- memif_conn_handle_t c = NULL;
- memif_conn_args_t args;
- memset (&args, 0, sizeof (args));
-
- args.interface_id = 0;
- args.is_master = 0;
- args.mode = 0;
-
- if ((err =
- memif_init (control_fd_update, TEST_APP_NAME, NULL,
- NULL, NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- if ((err = memif_create (&c, &args, on_connect,
- on_disconnect, on_interrupt,
- NULL)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_connection_t *conn = (memif_connection_t *) c;
-
- conn->run_args.num_s2m_rings = 1;
- conn->run_args.num_m2s_rings = 1;
- conn->run_args.log2_ring_size = 10;
- conn->run_args.buffer_size = 2048;
-
- if ((err = memif_init_regions_and_queues (conn)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- memif_msg_t msg;
- memset (&msg, 0, sizeof (msg));
- msg.type = MEMIF_MSG_TYPE_CONNECT;
-
- memset (msg.connect.if_name, 0, sizeof (msg.connect.if_name));
- strncpy ((char *) msg.connect.if_name, TEST_IF_NAME, strlen (TEST_IF_NAME));
-
- if ((err = memif_msg_receive_connected (conn, &msg)) != MEMIF_ERR_SUCCESS)
- ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_str_eq ((char *)conn->remote_if_name, TEST_IF_NAME);
-}
-
-END_TEST
-START_TEST (test_recv_disconnect)
-{
- int err;
- memif_connection_t conn;
- memif_msg_t msg;
- msg.type = MEMIF_MSG_TYPE_DISCONNECT;
- memset (msg.disconnect.string, 0, sizeof (msg.disconnect.string));
- strncpy ((char *) msg.disconnect.string, "unit_test_dc", 12);
-
- if ((err = memif_msg_receive_disconnect (&conn, &msg)) != MEMIF_ERR_SUCCESS)
- ck_assert_msg (err == MEMIF_ERR_DISCONNECT,
- "err code: %u, err msg: %s", err, memif_strerror (err));
-
- ck_assert_str_eq ((char *)conn.remote_disconnect_string, "unit_test_dc");
-}
-
-END_TEST Suite *
-socket_suite ()
-{
- Suite *s;
- TCase *tc_msg_queue;
- TCase *tc_msg_enq;
- TCase *tc_msg_send;
- TCase *tc_msg_recv;
-
- /* create socket test suite */
- s = suite_create ("Socket messaging");
-
- /* create msg queue test case */
- tc_msg_queue = tcase_create ("Message queue");
- /* add tests to test case */
- tcase_add_test (tc_msg_queue, test_msg_queue);
-
- /* create msg enq test case */
- tc_msg_enq = tcase_create ("Message enqueue");
- /* add tests to test case */
- tcase_add_test (tc_msg_enq, test_enq_ack);
- tcase_add_test (tc_msg_enq, test_enq_init);
- tcase_add_test (tc_msg_enq, test_enq_add_region);
- tcase_add_test (tc_msg_enq, test_enq_add_ring);
- tcase_add_test (tc_msg_enq, test_enq_connect);
- tcase_add_test (tc_msg_enq, test_enq_connected);
-
- /* create msg send test case */
- tc_msg_send = tcase_create ("Message send");
- /* add tests to test case */
- tcase_add_test (tc_msg_send, test_send);
- tcase_add_test (tc_msg_send, test_send_hello);
- tcase_add_test (tc_msg_send, test_send_disconnect);
-
- /* create msg recv test case */
- tc_msg_recv = tcase_create ("Message receive");
- /* add tests to test case */
- tcase_add_test (tc_msg_recv, test_recv_hello);
- tcase_add_test (tc_msg_recv, test_recv_init);
- tcase_add_test (tc_msg_recv, test_recv_add_region);
- tcase_add_test (tc_msg_recv, test_recv_add_ring);
- tcase_add_test (tc_msg_recv, test_recv_connect);
- tcase_add_test (tc_msg_recv, test_recv_connected);
- tcase_add_test (tc_msg_recv, test_recv_disconnect);
-
- /* add test cases to test suite */
- suite_add_tcase (s, tc_msg_queue);
- suite_add_tcase (s, tc_msg_enq);
- suite_add_tcase (s, tc_msg_send);
- suite_add_tcase (s, tc_msg_recv);
-
- /* return socket test suite to test runner */
- return s;
-}
diff --git a/extras/libmemif/test/suite_main/CMakeLists.txt b/extras/libmemif/test/suite_main/CMakeLists.txt
new file mode 100644
index 00000000000..7a2940098e0
--- /dev/null
+++ b/extras/libmemif/test/suite_main/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.0)
+
+set (This MemifMainTest)
+
+set (Sources
+ memif_main_test.c)
+
+add_executable(${This} ${Sources})
+
+target_link_libraries(${This} PUBLIC
+ unity
+ memif
+)
+
+add_test(
+ NAME ${This}
+ COMMAND ${This}
+)
diff --git a/extras/libmemif/test/suite_main/memif_main_test.c b/extras/libmemif/test/suite_main/memif_main_test.c
new file mode 100644
index 00000000000..d6b2e792de2
--- /dev/null
+++ b/extras/libmemif/test/suite_main/memif_main_test.c
@@ -0,0 +1,388 @@
+#include <main.c>
+#include <unity_fixture.h>
+#include <memif_private.h>
+#include <stdlib.h>
+
+#define MEMIF_VERSION_STR "2.0"
+#define MEMIF_BUFFER_SIZE 2048
+#define MEMIF_INTERFACE_NAME "memif0/0"
+#define MEMIF_LOG_RING_SIZE 10
+#define MEMIF_SECRET "psst"
+
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+static memif_socket_handle_t socket_handler;
+static memif_conn_handle_t conn;
+
+static memif_socket_args_t memif_socket_args;
+static memif_conn_args_t memif_conn_args;
+
+static int dummy_on_connect (void *, void *);
+static int dummy_on_disconnect (void *, void *);
+static int dummy_on_interrupt (void *, void *, uint16_t);
+
+static int
+dummy_on_connect (void *a, void *b)
+{
+}
+static int
+dummy_on_disconnect (void *a, void *b)
+{
+}
+static int
+dummy_on_interrupt (void *a, void *b, uint16_t c)
+{
+}
+
+static void
+init_conn_args ()
+{
+ memif_connection_t *c = (memif_connection_t *) conn;
+ memset (c, 0, sizeof (memif_connection_t));
+ c->args = (memif_conn_args_t){ .buffer_size = MEMIF_BUFFER_SIZE,
+ .interface_id = 0,
+ .interface_name = MEMIF_INTERFACE_NAME,
+ .is_master = 0,
+ .log2_ring_size = MEMIF_LOG_RING_SIZE,
+ .mode = 0,
+ .num_m2s_rings = 1,
+ .num_s2m_rings = 1,
+ .secret = MEMIF_SECRET,
+ .socket = &socket_handler };
+}
+
+static void
+init_connection ()
+{
+ conn = malloc (sizeof (memif_connection_t));
+ memif_connection_t *c = (memif_connection_t *) conn;
+ init_conn_args ();
+}
+
+static void
+init_socket ()
+{
+ memif_socket_t *ms = malloc (sizeof (memif_socket_t));
+ socket_handler = ms;
+ /* default values */
+ memset (ms, 0, sizeof (memif_socket_t));
+ ms->epfd = 3;
+ ms->listener_fd = 4;
+ ms->poll_cancel_fd = 5;
+ ms->timer_fd = -1;
+
+ TAILQ_INIT (&ms->master_interfaces);
+ TAILQ_INIT (&ms->slave_interfaces);
+}
+
+static void
+init_socket_args ()
+{
+ strncpy (memif_socket_args.app_name, MEMIF_DEFAULT_APP_NAME,
+ strlen (MEMIF_DEFAULT_APP_NAME));
+ strncpy (memif_socket_args.path, MEMIF_DEFAULT_SOCKET_PATH,
+ strlen (MEMIF_DEFAULT_SOCKET_PATH));
+}
+
+static void
+delete_connection ()
+{
+ free (conn);
+}
+
+TEST_GROUP (MemifMain);
+
+TEST_SETUP (MemifMain) {}
+
+TEST_TEAR_DOWN (MemifMain) {}
+
+TEST (MemifMain, MemifGetVersion)
+{
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_VERSION, memif_get_version ());
+}
+
+TEST (MemifMain, MemifGetVersionStr)
+{
+ TEST_ASSERT_EQUAL_STRING (MEMIF_VERSION_STR, memif_get_version_str ());
+}
+
+TEST (MemifMain, MemifStrError)
+{
+ for (size_t i = 0; i < ERRLIST_LEN; i++)
+ {
+ TEST_ASSERT_EQUAL_STRING (memif_strerror (i), memif_errlist[i]);
+ }
+ TEST_ASSERT_EQUAL_STRING (memif_strerror (ERRLIST_LEN + 1),
+ MEMIF_ERR_UNDEFINED);
+}
+
+TEST (MemifMain, MemifGetDetails)
+{
+ init_socket ();
+ init_connection ();
+ memif_details_t md;
+ ssize_t buflen = 2048;
+ char buf[buflen];
+ memif_get_details (conn, &md, buf, buflen);
+
+ TEST_ASSERT_EQUAL_STRING (MEMIF_INTERFACE_NAME, md.if_name);
+ TEST_ASSERT_EQUAL_UINT32 (0, md.id);
+ TEST_ASSERT_EQUAL_STRING (MEMIF_SECRET, md.secret);
+ TEST_ASSERT_EQUAL_UINT8 (1, md.role);
+ TEST_ASSERT_EQUAL_UINT8 (0, md.mode);
+ TEST_ASSERT_EQUAL_UINT8 (0, md.link_up_down);
+}
+
+TEST (MemifMain, MemifControl_fd_update_add_del_epoll_fd)
+{
+ init_socket_args ();
+ memif_create_socket (&socket_handler, &memif_socket_args, NULL);
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ void *ctx;
+ memif_socket_t *ms = (memif_socket_t *) socket_handler;
+
+ fdata = ms->args.alloc (sizeof (*fdata));
+ fdata->event_handler = memif_poll_cancel_handler;
+ fdata->private_ctx = ms;
+
+ fde.fd = eventfd (0, EFD_NONBLOCK);
+ fde.private_ctx = fdata;
+ fde.type = MEMIF_FD_EVENT_READ;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ TEST_ASSERT_EQUAL_INT (0, memif_control_fd_update (fde, ctx));
+ fde.type = MEMIF_FD_EVENT_DEL;
+ TEST_ASSERT_EQUAL_INT (0, memif_control_fd_update (fde, ctx));
+}
+
+TEST (MemifMain, MemifSetConnectionRequestTimer)
+{
+ memif_socket_handle_t msh =
+ (memif_socket_handle_t) malloc (sizeof (memif_socket_t));
+ memif_socket_t *ms = (memif_socket_t *) msh;
+ struct itimerspec timer;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ int i, err = MEMIF_ERR_SUCCESS;
+ void *ctx;
+ memset (ms, 0, sizeof (memif_socket_t));
+ ms->epfd = -1;
+ ms->listener_fd = -1;
+ ms->poll_cancel_fd = -1;
+
+ TAILQ_INIT (&ms->master_interfaces);
+ TAILQ_INIT (&ms->slave_interfaces);
+ ms->timer_fd = -1;
+ ms->args.alloc = malloc;
+ ms->args.free = free;
+ ms->args.realloc = realloc;
+
+ if (ms->args.on_control_fd_update == NULL)
+ {
+ ms->epfd = epoll_create (1);
+ memif_control_fd_update_register (ms, memif_control_fd_update);
+ ms->poll_cancel_fd = eventfd (0, EFD_NONBLOCK);
+
+ fdata = ms->args.alloc (sizeof (*fdata));
+ fdata->event_handler = memif_poll_cancel_handler;
+ fdata->private_ctx = ms;
+
+ fde.fd = ms->poll_cancel_fd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
+ }
+
+ timer.it_value.tv_sec = 2;
+ timer.it_value.tv_nsec = 0;
+ timer.it_interval.tv_sec = 2;
+ timer.it_value.tv_nsec = 0;
+ memif_set_connection_request_timer (msh, timer);
+
+ TEST_ASSERT_NOT_EQUAL_INT (-1, ms->timer_fd);
+ memif_delete_socket (&msh);
+}
+
+TEST (MemifMain, MemifSetConnectionRequestTimerNoTimer)
+{
+ memif_socket_handle_t msh =
+ (memif_socket_handle_t) malloc (sizeof (memif_socket_t));
+ memif_socket_t *ms = (memif_socket_t *) msh;
+ struct itimerspec timer;
+ memif_fd_event_t fde;
+ memif_fd_event_data_t *fdata;
+ int i, err = MEMIF_ERR_SUCCESS;
+ void *ctx;
+ memset (ms, 0, sizeof (memif_socket_t));
+ ms->epfd = -1;
+ ms->listener_fd = -1;
+ ms->poll_cancel_fd = -1;
+
+ TAILQ_INIT (&ms->master_interfaces);
+ TAILQ_INIT (&ms->slave_interfaces);
+ ms->timer_fd = -1;
+ ms->args.alloc = malloc;
+ ms->args.free = free;
+ ms->args.realloc = realloc;
+
+ if (ms->args.on_control_fd_update == NULL)
+ {
+ ms->epfd = epoll_create (1);
+ /* register default fd update callback */
+ memif_control_fd_update_register (ms, memif_control_fd_update);
+ ms->poll_cancel_fd = eventfd (0, EFD_NONBLOCK);
+ if (ms->poll_cancel_fd < 0)
+ {
+ err = errno;
+ DBG ("eventfd: %s", strerror (err));
+ // return memif_syscall_error_handler (err);
+ }
+ /* add interrupt fd to epfd */
+ fdata = ms->args.alloc (sizeof (*fdata));
+ fdata->event_handler = memif_poll_cancel_handler;
+ fdata->private_ctx = ms;
+
+ fde.fd = ms->poll_cancel_fd;
+ fde.type = MEMIF_FD_EVENT_READ;
+ fde.private_ctx = fdata;
+ ctx = ms->epfd != -1 ? ms : ms->private_ctx;
+ ms->args.on_control_fd_update (fde, ctx);
+ }
+ memset (&timer, 0, sizeof (struct itimerspec));
+ memif_set_connection_request_timer (msh, timer);
+
+ TEST_ASSERT_EQUAL_INT (-1, ms->timer_fd);
+ memif_delete_socket (msh);
+ memif_delete_socket (&msh);
+}
+
+TEST_GROUP (MemifInterface);
+
+TEST_SETUP (MemifInterface)
+{
+ socket_handler = NULL;
+ conn = NULL;
+ memset (&memif_socket_args, 0, sizeof (memif_socket_args_t));
+ memset (&memif_conn_args, 0, sizeof (memif_conn_args_t));
+
+ memif_socket_args = (memif_socket_args_t){
+ .app_name = "TEST",
+ .path = "@memif.sock",
+ };
+ int err = memif_create_socket (&socket_handler, &memif_socket_args, NULL);
+ if (err)
+ exit (EXIT_FAILURE);
+ memif_conn_args.socket = socket_handler;
+ memif_conn_args.interface_id = 0;
+ strncpy (memif_conn_args.interface_name, MEMIF_INTERFACE_NAME,
+ sizeof (memif_conn_args.interface_name));
+}
+
+TEST_TEAR_DOWN (MemifInterface)
+{
+ memif_delete (&conn);
+ memif_delete_socket (&socket_handler);
+ memset (&memif_socket_args, 0, sizeof (memif_socket_args_t));
+ memset (&memif_conn_args, 0, sizeof (memif_conn_args_t));
+ memif_delete (&conn);
+}
+
+TEST (MemifInterface, MemifCreateMaster)
+{
+ memif_conn_args.is_master = 1;
+ int err = memif_create (&conn, &memif_conn_args, dummy_on_connect,
+ dummy_on_disconnect, dummy_on_interrupt, NULL);
+
+ TEST_ASSERT_EQUAL_INT (MEMIF_ERR_SUCCESS, err);
+
+ memif_socket_t *ms = (memif_socket_t *) socket_handler;
+ memif_connection_t *mc = conn;
+
+ TEST_ASSERT_NULL (ms->slave_interfaces.tqh_first);
+ TEST_ASSERT_NOT_NULL (ms->master_interfaces.tqh_first);
+ TEST_ASSERT_NOT_NULL (mc->args.socket);
+ TEST_ASSERT_EQUAL_INT (mc->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
+ TEST_ASSERT_EQUAL_UINT8 (mc->args.log2_ring_size,
+ MEMIF_DEFAULT_LOG2_RING_SIZE);
+ TEST_ASSERT_EQUAL_UINT8 (mc->args.num_m2s_rings, 1);
+ TEST_ASSERT_EQUAL_UINT8 (mc->args.num_s2m_rings, 1);
+}
+TEST (MemifInterface, MemifCreateSlave)
+{
+ memif_conn_args.is_master = 0;
+
+ int err = memif_create (&conn, &memif_conn_args, dummy_on_connect,
+ dummy_on_disconnect, dummy_on_interrupt, NULL);
+
+ memif_socket_t *ms = (memif_socket_t *) socket_handler;
+ memif_connection_t *mc = conn;
+
+ TEST_ASSERT_EQUAL_INT (MEMIF_ERR_SUCCESS, err);
+ TEST_ASSERT_NULL (ms->master_interfaces.tqh_first);
+ TEST_ASSERT_NOT_NULL (ms->slave_interfaces.tqh_first);
+ TEST_ASSERT_NOT_NULL (mc->args.socket);
+ TEST_ASSERT_EQUAL_INT (mc->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
+ TEST_ASSERT_EQUAL_UINT8 (mc->args.log2_ring_size,
+ MEMIF_DEFAULT_LOG2_RING_SIZE);
+ TEST_ASSERT_EQUAL_UINT8 (mc->args.num_m2s_rings, 1);
+ TEST_ASSERT_EQUAL_UINT8 (mc->args.num_s2m_rings, 1);
+}
+
+TEST (MemifInterface, MemifDelete)
+{
+ memif_conn_args.is_master = 0;
+
+ memif_create (&conn, &memif_conn_args, dummy_on_connect, dummy_on_disconnect,
+ dummy_on_interrupt, NULL);
+
+ int err = memif_delete (&conn);
+
+ TEST_ASSERT_EQUAL_INT (MEMIF_ERR_SUCCESS, err);
+ TEST_ASSERT_NULL (conn);
+}
+
+TEST (MemifMain, MemifPollEvent)
+{
+ init_socket_args ();
+ memif_create_socket (&socket_handler, &memif_socket_args, NULL);
+ memif_socket_t *ms = (memif_socket_t *) socket_handler;
+ uint64_t buf = 1;
+ int ret = write (ms->poll_cancel_fd, &buf, sizeof (buf));
+ TEST_ASSERT_EQUAL (8, ret);
+ TEST_ASSERT_EQUAL (MEMIF_ERR_POLL_CANCEL,
+ memif_poll_event (socket_handler, -1));
+}
+
+TEST_GROUP_RUNNER (MemifMain){
+ RUN_TEST_CASE (MemifMain, MemifGetVersion)
+ RUN_TEST_CASE (MemifMain, MemifGetVersionStr)
+ RUN_TEST_CASE (MemifMain, MemifStrError)
+ RUN_TEST_CASE (MemifMain, MemifGetDetails)
+ RUN_TEST_CASE (MemifMain, MemifControl_fd_update_add_del_epoll_fd)
+ RUN_TEST_CASE (MemifMain, MemifSetConnectionRequestTimer)
+ RUN_TEST_CASE (MemifMain, MemifSetConnectionRequestTimerNoTimer)
+ RUN_TEST_CASE (MemifMain, MemifPollEvent)
+}
+
+TEST_GROUP_RUNNER (MemifInterface)
+{
+ RUN_TEST_CASE (MemifInterface, MemifCreateMaster);
+ RUN_TEST_CASE (MemifInterface, MemifCreateSlave);
+ RUN_TEST_CASE (MemifInterface, MemifDelete);
+}
+static void
+RunAllTests (void)
+{
+ RUN_TEST_GROUP (MemifMain);
+ RUN_TEST_GROUP (MemifInterface);
+}
+
+int
+main (int argc, const char *argv[])
+{
+ return UnityMain (argc, argv, RunAllTests);
+}
diff --git a/extras/libmemif/test/suite_socket/CMakeLists.txt b/extras/libmemif/test/suite_socket/CMakeLists.txt
new file mode 100644
index 00000000000..5ac66a06cfa
--- /dev/null
+++ b/extras/libmemif/test/suite_socket/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.0)
+
+set (This MemifSocketTest)
+
+set (Sources
+ memif_socket_test.c)
+
+add_executable(${This} ${Sources})
+
+target_link_libraries(${This} PUBLIC
+ unity
+ memif
+)
+
+add_test(
+ NAME ${This}
+ COMMAND ${This}
+)
diff --git a/extras/libmemif/test/suite_socket/memif_socket_test.c b/extras/libmemif/test/suite_socket/memif_socket_test.c
new file mode 100644
index 00000000000..4e12314d48a
--- /dev/null
+++ b/extras/libmemif/test/suite_socket/memif_socket_test.c
@@ -0,0 +1,401 @@
+#include <main.c>
+#include <socket.c>
+
+#include <unity_fixture.h>
+#include <libmemif.h>
+#include <memif_private.h>
+#include <stdlib.h>
+
+#define TEST_APP_NAME "unit_test_app"
+#define MEMIF_TEST_IF_NAME "unit_test_if"
+#define MEMIF_TEST_SECRET "psst"
+#define TEST_IF_ID 0
+#define TEST_SOCKET_PATH "@memif.sock"
+
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+static int err;
+static memif_socket_handle_t memif_socket;
+static memif_socket_args_t memif_socket_args;
+static memif_control_channel_t *cc;
+
+static void
+init_socket_args ()
+{
+ strncpy (memif_socket_args.app_name, MEMIF_DEFAULT_APP_NAME,
+ strlen (MEMIF_DEFAULT_APP_NAME));
+ sprintf (memif_socket_args.path, "%s", MEMIF_DEFAULT_SOCKET_PATH);
+}
+
+TEST_GROUP (MemifSocket);
+
+TEST_SETUP (MemifSocket)
+{
+ memif_socket = NULL;
+ static int err = 0;
+}
+
+TEST_TEAR_DOWN (MemifSocket) {}
+
+TEST (MemifSocket, CreateSocket)
+{
+
+ memif_socket_args_t memif_socket_args = {
+ .app_name = TEST_APP_NAME,
+ .path = TEST_SOCKET_PATH,
+ };
+ err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
+
+ TEST_ASSERT_EQUAL_INT (0, err);
+ TEST_ASSERT_NOT_NULL (memif_socket);
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+
+ TEST_ASSERT_EQUAL_STRING (ms->args.app_name, TEST_APP_NAME);
+ TEST_ASSERT_EQUAL_STRING (ms->args.path, TEST_SOCKET_PATH);
+ TEST_ASSERT_EQUAL_PTR (ms->args.on_control_fd_update,
+ memif_control_fd_update);
+ TEST_ASSERT_EQUAL_PTR (ms->args.alloc, malloc);
+ TEST_ASSERT_EQUAL_PTR (ms->args.realloc, realloc);
+ TEST_ASSERT_EQUAL_PTR (ms->args.free, free);
+
+ TEST_ASSERT_NOT_EQUAL_INT (ms->epfd, -1);
+ TEST_ASSERT_NOT_EQUAL_INT (ms->poll_cancel_fd, -1);
+
+ memif_delete_socket (&memif_socket);
+}
+
+TEST (MemifSocket, DeleteSocket)
+{
+
+ memif_socket_args_t memif_socket_args = {
+ .app_name = TEST_APP_NAME,
+ .path = TEST_SOCKET_PATH,
+ };
+ memif_create_socket (&memif_socket, &memif_socket_args, NULL);
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+ err = memif_delete_socket (&memif_socket);
+ TEST_ASSERT_EQUAL_INT (MEMIF_ERR_SUCCESS, err);
+ TEST_ASSERT_NULL (memif_socket);
+}
+
+TEST_GROUP (MemifControlChannel);
+
+TEST_SETUP (MemifControlChannel)
+{
+ memif_socket = NULL;
+ static int err = 0;
+ init_socket_args ();
+ memif_create_socket (&memif_socket, &memif_socket_args, NULL);
+ cc = (memif_control_channel_t *) malloc (sizeof (memif_control_channel_t));
+}
+
+TEST_TEAR_DOWN (MemifControlChannel) { free (cc); }
+
+TEST (MemifControlChannel, EnqAck)
+{
+ memif_connection_t conn;
+ memif_msg_queue_elt_t *e;
+ cc->fd = 5;
+ cc->conn = NULL;
+ cc->sock = memif_socket;
+
+ TAILQ_INIT (&cc->msg_queue);
+ memif_msg_enq_ack (cc);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_ACK, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (-1, e->fd);
+}
+
+TEST (MemifControlChannel, EnqHello)
+{
+ memif_connection_t conn;
+ memif_msg_queue_elt_t *e;
+ cc->fd = 5;
+ cc->conn = NULL;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ memif_msg_enq_hello (cc);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_HELLO, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (-1, e->fd);
+ memif_msg_hello_t h = e->msg.hello;
+ TEST_ASSERT_EQUAL_INT (MEMIF_MAX_LOG2_RING_SIZE, h.max_log2_ring_size);
+ TEST_ASSERT_EQUAL_INT (MEMIF_VERSION, h.min_version);
+ TEST_ASSERT_EQUAL_INT (MEMIF_VERSION, h.max_version);
+ TEST_ASSERT_EQUAL_INT (MEMIF_MAX_S2M_RING, h.max_s2m_ring);
+ TEST_ASSERT_EQUAL_INT (MEMIF_MAX_M2S_RING, h.max_m2s_ring);
+ TEST_ASSERT_EQUAL_INT (MEMIF_MAX_REGION, h.max_region);
+}
+
+TEST (MemifControlChannel, EnqInit)
+{
+ memif_msg_queue_elt_t *e;
+ memif_connection_t conn;
+ cc->fd = 5;
+ cc->conn = &conn;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ conn.args.interface_id = 11;
+ conn.args.mode = 1;
+ strlcpy ((char *) conn.args.secret, MEMIF_TEST_SECRET,
+ sizeof (MEMIF_TEST_SECRET));
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+
+ memif_msg_enq_init (cc);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_INIT, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (-1, e->fd);
+ memif_msg_init_t h = e->msg.init;
+
+ TEST_ASSERT_EQUAL_INT (11, h.id);
+ TEST_ASSERT_EQUAL_INT (1, h.mode);
+ TEST_ASSERT_EQUAL_STRING (MEMIF_DEFAULT_APP_NAME, h.name);
+ TEST_ASSERT_EQUAL_STRING (MEMIF_TEST_SECRET, h.secret);
+ TEST_ASSERT_EQUAL_INT (MEMIF_VERSION, h.version);
+}
+
+TEST (MemifControlChannel, EnqAddRegion)
+{
+ memif_msg_queue_elt_t *e;
+ memif_connection_t conn;
+ memset (cc, 0, sizeof (memif_msg_queue_elt_t));
+ memset (&conn, 0, sizeof (memif_connection_t));
+
+ cc->fd = 5;
+ cc->conn = &conn;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ conn.args.interface_id = 11;
+ conn.args.mode = 1;
+ conn.args.socket = memif_socket;
+ strlcpy ((char *) conn.args.secret, MEMIF_TEST_SECRET,
+ sizeof (MEMIF_TEST_SECRET));
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+
+ conn.run_args.num_s2m_rings = 1;
+ conn.run_args.num_m2s_rings = 1;
+ conn.run_args.log2_ring_size = MEMIF_DEFAULT_LOG2_RING_SIZE;
+ conn.run_args.buffer_size = MEMIF_DEFAULT_BUFFER_SIZE;
+
+ memif_add_region (&conn, 1);
+
+ memif_msg_enq_add_region (cc, 0);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+ memif_msg_add_region_t h = e->msg.add_region;
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_ADD_REGION, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (conn.regions[0].fd, e->fd);
+
+ TEST_ASSERT_EQUAL_INT (0, h.index);
+ TEST_ASSERT_EQUAL_INT (conn.regions[0].region_size, h.size);
+
+ close (conn.regions[0].fd);
+}
+
+TEST (MemifControlChannel, EnqAddRing)
+{
+ memif_msg_queue_elt_t *e;
+ memif_connection_t conn;
+ memset (cc, 0, sizeof (memif_msg_queue_elt_t));
+ memset (&conn, 0, sizeof (memif_connection_t));
+
+ cc->fd = 5;
+ cc->conn = &conn;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ conn.args.interface_id = 11;
+ conn.args.mode = 1;
+ conn.args.socket = memif_socket;
+ strlcpy ((char *) conn.args.secret, MEMIF_TEST_SECRET,
+ sizeof (MEMIF_TEST_SECRET));
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+
+ conn.run_args.num_s2m_rings = 1;
+ conn.run_args.num_m2s_rings = 1;
+ conn.run_args.log2_ring_size = MEMIF_DEFAULT_LOG2_RING_SIZE;
+ conn.run_args.buffer_size = MEMIF_DEFAULT_BUFFER_SIZE;
+
+ memif_add_region (&conn, 1);
+ memif_init_queues (&conn);
+ memif_msg_enq_add_ring (cc, 0, MEMIF_RING_M2S);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+ memif_msg_add_ring_t h = e->msg.add_ring;
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_ADD_RING, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (conn.rx_queues[0].int_fd, e->fd);
+
+ TEST_ASSERT_EQUAL_INT (0, h.flags);
+ TEST_ASSERT_EQUAL_INT (0, h.index);
+ TEST_ASSERT_EQUAL_INT (conn.rx_queues[0].region, h.region);
+ TEST_ASSERT_EQUAL_INT (conn.rx_queues[0].offset, h.offset);
+ TEST_ASSERT_EQUAL_INT (conn.rx_queues[0].log2_ring_size, h.log2_ring_size);
+ TEST_ASSERT_EQUAL_INT (0, h.private_hdr_size);
+
+ close (conn.regions[0].fd);
+}
+TEST (MemifControlChannel, EnqConnect)
+{
+ memif_msg_queue_elt_t *e;
+ memif_connection_t conn;
+ memset (cc, 0, sizeof (memif_msg_queue_elt_t));
+ memset (&conn, 0, sizeof (memif_connection_t));
+
+ cc->fd = 5;
+ cc->conn = &conn;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ conn.args.interface_id = 11;
+ conn.args.mode = 1;
+ conn.args.socket = memif_socket;
+ strlcpy ((char *) conn.args.secret, MEMIF_TEST_SECRET,
+ sizeof (MEMIF_TEST_SECRET));
+ strlcpy ((char *) conn.args.interface_name, MEMIF_TEST_IF_NAME,
+ sizeof (MEMIF_TEST_IF_NAME));
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+
+ conn.run_args.num_s2m_rings = 1;
+ conn.run_args.num_m2s_rings = 1;
+ conn.run_args.log2_ring_size = MEMIF_DEFAULT_LOG2_RING_SIZE;
+ conn.run_args.buffer_size = MEMIF_DEFAULT_BUFFER_SIZE;
+
+ memif_add_region (&conn, 1);
+ memif_init_queues (&conn);
+ memif_msg_enq_connect (cc);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+ memif_msg_connect_t h = e->msg.connect;
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_CONNECT, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (-1, e->fd);
+
+ TEST_ASSERT_EQUAL_STRING (MEMIF_TEST_IF_NAME, h.if_name);
+
+ close (conn.regions[0].fd);
+}
+
+TEST (MemifControlChannel, EnqConnected)
+{
+ memif_msg_queue_elt_t *e;
+ memif_connection_t conn;
+ memset (cc, 0, sizeof (memif_msg_queue_elt_t));
+ memset (&conn, 0, sizeof (memif_connection_t));
+
+ cc->fd = 5;
+ cc->conn = &conn;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ conn.args.interface_id = 11;
+ conn.args.mode = 1;
+ conn.args.socket = memif_socket;
+ strlcpy ((char *) conn.args.secret, MEMIF_TEST_SECRET,
+ sizeof (MEMIF_TEST_SECRET));
+ strlcpy ((char *) conn.args.interface_name, MEMIF_TEST_IF_NAME,
+ sizeof (MEMIF_TEST_IF_NAME));
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+
+ conn.run_args.num_s2m_rings = 1;
+ conn.run_args.num_m2s_rings = 1;
+ conn.run_args.log2_ring_size = MEMIF_DEFAULT_LOG2_RING_SIZE;
+ conn.run_args.buffer_size = MEMIF_DEFAULT_BUFFER_SIZE;
+
+ memif_add_region (&conn, 1);
+ memif_init_queues (&conn);
+ memif_msg_enq_connect (cc);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+ memif_msg_connected_t h = e->msg.connected;
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_CONNECT, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (-1, e->fd);
+
+ TEST_ASSERT_EQUAL_STRING (MEMIF_TEST_IF_NAME, h.if_name);
+
+ close (conn.regions[0].fd);
+}
+
+TEST (MemifControlChannel, EnqDisconnect)
+{
+ memif_msg_queue_elt_t *e;
+ memif_connection_t conn;
+ memset (cc, 0, sizeof (memif_msg_queue_elt_t));
+ memset (&conn, 0, sizeof (memif_connection_t));
+
+ cc->fd = 5;
+ cc->conn = &conn;
+ cc->sock = memif_socket;
+ TAILQ_INIT (&cc->msg_queue);
+
+ conn.args.interface_id = 11;
+ conn.args.mode = 1;
+ conn.args.socket = memif_socket;
+ strlcpy ((char *) conn.args.secret, MEMIF_TEST_SECRET,
+ sizeof (MEMIF_TEST_SECRET));
+ strlcpy ((char *) conn.args.interface_name, MEMIF_TEST_IF_NAME,
+ sizeof (MEMIF_TEST_IF_NAME));
+
+ memif_socket_t *ms = (memif_socket_t *) memif_socket;
+ memif_msg_enq_disconnect (cc, "TEST", 5);
+
+ e = TAILQ_FIRST (&cc->msg_queue);
+ memif_msg_disconnect_t h = e->msg.disconnect;
+
+ TEST_ASSERT_EQUAL_UINT16 (MEMIF_MSG_TYPE_DISCONNECT, e->msg.type);
+ TEST_ASSERT_EQUAL_INT (-1, e->fd);
+
+ TEST_ASSERT_EQUAL_INT (5, h.code);
+ TEST_ASSERT_EQUAL_STRING ("TEST", h.string);
+}
+
+TEST_GROUP_RUNNER (MemifSocket){ RUN_TEST_CASE (MemifSocket, CreateSocket)
+ RUN_TEST_CASE (MemifSocket, DeleteSocket)
+
+}
+
+TEST_GROUP_RUNNER (MemifControlChannel)
+{
+ RUN_TEST_CASE (MemifControlChannel, EnqAck)
+ RUN_TEST_CASE (MemifControlChannel, EnqHello)
+ RUN_TEST_CASE (MemifControlChannel, EnqInit)
+ RUN_TEST_CASE (MemifControlChannel, EnqAddRegion)
+ RUN_TEST_CASE (MemifControlChannel, EnqAddRing)
+ RUN_TEST_CASE (MemifControlChannel, EnqConnect)
+ RUN_TEST_CASE (MemifControlChannel, EnqConnected)
+ RUN_TEST_CASE (MemifControlChannel, EnqDisconnect)
+}
+
+static void
+RunAllTests (void)
+{
+ RUN_TEST_GROUP (MemifSocket);
+ RUN_TEST_GROUP (MemifControlChannel);
+}
+
+int
+main (int argc, const char *argv[])
+{
+ return UnityMain (argc, argv, RunAllTests);
+}
diff --git a/extras/libmemif/test/unit_test.c b/extras/libmemif/test/unit_test.c
deleted file mode 100644
index 0ae045b9573..00000000000
--- a/extras/libmemif/test/unit_test.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 <main_test.h>
-#include <socket_test.h>
-
-int
-on_connect (memif_conn_handle_t conn, void *ctx)
-{
- return 0;
-}
-
-int
-on_disconnect (memif_conn_handle_t conn, void *ctx)
-{
- return 0;
-}
-
-int
-on_interrupt (memif_conn_handle_t conn, void *ctx, uint16_t qid)
-{
- return 0;
-}
-
-int
-control_fd_update (int fd, uint8_t events, void *ctx)
-{
- return 0;
-}
-
-int
-main (void)
-{
- int num_fail;
- Suite *mains, *socket;
- SRunner *sr;
-
- mains = main_suite ();
- socket = socket_suite ();
-
- sr = srunner_create (mains);
-
- srunner_add_suite (sr, socket);
-
- srunner_run_all (sr, CK_VERBOSE);
- num_fail = srunner_ntests_failed (sr);
- srunner_free (sr);
- return (num_fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/extras/libmemif/test/unit_test.h b/extras/libmemif/test/unit_test.h
deleted file mode 100644
index 3182c77e6e6..00000000000
--- a/extras/libmemif/test/unit_test.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *------------------------------------------------------------------
- * Copyright (c) 2017 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 _UNIT_TEST_H_
-#define _UNIT_TEST_H_
-
-#include <stdlib.h>
-#include <check.h>
-
-#include <memif.h>
-#include <libmemif.h>
-
-#define TEST_APP_NAME "unit_test_app"
-#define TEST_IF_NAME "unit_test_if"
-#define TEST_SECRET "psst"
-
-int on_connect (memif_conn_handle_t conn, void *ctx);
-
-int on_disconnect (memif_conn_handle_t conn, void *ctx);
-
-int on_interrupt (memif_conn_handle_t conn, void *ctx, uint16_t qid);
-
-int control_fd_update (int fd, uint8_t events, void *ctx);
-
-#endif /* _UNIT_TEST_H_ */
diff --git a/extras/packetforge/Edge.py b/extras/packetforge/Edge.py
new file mode 100644
index 00000000000..af9de340047
--- /dev/null
+++ b/extras/packetforge/Edge.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2022 Intel 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.
+
+from EdgeAction import *
+import json
+
+
+class Edge:
+ def __init__(self):
+ self.JSON = None
+ self.Start = None
+ self.End = None
+ self.actionList = []
+
+ def Create(jsonfile):
+ f = open(jsonfile, "r", encoding="utf-8")
+ token = json.load(f)
+
+ if token == None:
+ return None
+
+ if token["type"] != "edge":
+ return None
+
+ edgeList = []
+
+ startNodes = token["start"]
+ endNodes = token["end"]
+
+ if startNodes == None or endNodes == None:
+ return None
+
+ startTokens = startNodes.split(",")
+ endTokens = endNodes.split(",")
+
+ for start in startTokens:
+ for end in endTokens:
+ edge = Edge()
+
+ edge.Start = start
+ edge.End = end
+
+ if "actions" in token:
+ for at in token["actions"]:
+ action = EdgeAction.Create(at)
+ if not action:
+ return None
+
+ edge.actionList.append(action)
+
+ edge.JSON = jsonfile
+ edgeList.append(edge)
+
+ return edgeList
+
+ def Apply(self, first, second):
+ exp = []
+
+ for i in range(len(self.actionList)):
+ act = self.actionList[i]
+
+ if act.FromStartObject == True:
+ exp.append(first.GetValue(act.FromExpression))
+ elif act.FromStartObject == False:
+ exp.append(second.GetValue(act.FromExpression))
+ else:
+ exp.append(act.FromExpression)
+
+ for i in range(len(exp)):
+ act = self.actionList[i]
+
+ if act.ToStartObject:
+ first.SetFieldAuto(act.ToExpression, exp[i])
+ else:
+ second.SetFieldAuto(act.ToExpression, exp[i])
+
+ def Actions(self):
+ return self.actionList
+
+ def Name(self):
+ return self.Start + "_" + self.End
diff --git a/extras/packetforge/EdgeAction.py b/extras/packetforge/EdgeAction.py
new file mode 100644
index 00000000000..cf170088a5b
--- /dev/null
+++ b/extras/packetforge/EdgeAction.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2022 Intel 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.
+
+
+class EdgeAction:
+ def __init__(self):
+ self.ToStartObject = None
+ self.ToExpression = None
+ self.FromStartObject = None
+ self.FromExpression = None
+
+ def Create(token):
+ if token == None:
+ return None
+
+ dststr = token["dst"]
+ srcstr = token["src"]
+
+ if srcstr == None or dststr == None:
+ return None
+
+ action = EdgeAction()
+
+ dststr = dststr.strip()
+ srcstr = srcstr.strip()
+
+ if dststr.startswith("start."):
+ action.ToStartObject = True
+ action.ToExpression = dststr[6:]
+ elif dststr.startswith("end."):
+ action.ToStartObject = False
+ action.ToExpression = dststr[4:]
+ else:
+ return None
+
+ if srcstr.startswith("start."):
+ action.FromStartObject = True
+ action.FromExpression = srcstr[6:]
+ elif srcstr.startswith("end."):
+ action.FromStartObject = False
+ action.FromExpression = srcstr[4:]
+ else:
+ action.FromExpression = srcstr
+
+ return action
diff --git a/extras/packetforge/ExpressionConverter.py b/extras/packetforge/ExpressionConverter.py
new file mode 100644
index 00000000000..1f9c73fedfa
--- /dev/null
+++ b/extras/packetforge/ExpressionConverter.py
@@ -0,0 +1,162 @@
+# Copyright (c) 2022 Intel 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.
+
+from InputFormat import InputFormat
+
+
+def ByteArrayToString(data):
+ if len(data) == 0:
+ return ""
+
+ sb = []
+
+ for i in range(len(data) - 1):
+ sb.append("%02x" % data[i])
+
+ sb.append("%02x" % data[len(data) - 1])
+
+ return sb
+
+
+def ToNum(exp):
+ if exp == None:
+ return True, None
+
+ exp = exp.strip()
+
+ if exp.startswith("0x"):
+ out = int(exp, 16)
+ else:
+ try:
+ out = int(exp)
+ except:
+ return False, None
+
+ return True, out
+
+
+def ToIPv4Address(exp):
+ ipv4 = [0] * 4
+
+ exp = exp.strip()
+ tokens = exp.split(".")
+
+ if len(tokens) != 4:
+ return False, bytes(4)
+
+ for i in range(4):
+ u8 = int(tokens[i])
+ if u8 == None:
+ return False, bytes(4)
+
+ ipv4[i] = u8
+
+ return True, bytes(ipv4)
+
+
+def ToIPv6Address(exp):
+ ipv6 = [0] * 16
+
+ exp = exp.strip()
+ tokens = exp.split(":")
+
+ if len(tokens) != 8:
+ return False, bytes(16)
+
+ for i in range(8):
+ u16 = int(tokens[i], 16)
+ if u16 == None:
+ return False, bytes(16)
+
+ ipv6[i * 2] = u16 >> 8
+ ipv6[i * 2 + 1] = u16 & 0xFF
+
+ return True, bytes(ipv6)
+
+
+def ToMacAddress(exp):
+ mac = [0] * 6
+
+ exp = exp.strip()
+ tokens = exp.split(":")
+
+ if len(tokens) != 6:
+ return False, bytes(6)
+
+ for i in range(6):
+ u8 = int(tokens[i], 16)
+ if u8 == None:
+ return False, bytes(6)
+
+ mac[i] = u8
+
+ return True, bytes(mac)
+
+
+def ToByteArray(exp):
+ exp = exp.strip()
+ tokens = exp.split(",")
+
+ tmp = [] * len(tokens)
+
+ for i in range(len(tokens)):
+ _, num = ToNum(tokens[i])
+ if num == 0:
+ return False, bytes(len(tokens))
+
+ tmp[i] = ToNum(tokens[i])
+
+ return True, bytes(tmp)
+
+
+def Verify(format, expression):
+ if (
+ format == InputFormat.u8
+ or format == InputFormat.u16
+ or format == InputFormat.u32
+ or format == InputFormat.u64
+ ):
+ return ToNum(expression)
+ elif format == InputFormat.ipv4:
+ return ToIPv4Address(expression)
+ elif format == InputFormat.ipv6:
+ return ToIPv6Address(expression)
+ elif format == InputFormat.mac:
+ return ToMacAddress(expression)
+ elif format == InputFormat.bytearray:
+ return ToByteArray(expression)
+ else:
+ return False, 0
+
+
+def IncreaseValue(expression, size):
+ if expression == None:
+ return str(size)
+
+ _, num = ToNum(expression)
+ return str(num + size)
+
+
+def Equal(exp, val):
+ if exp == None:
+ num_1 = 0
+ else:
+ _, num_1 = ToNum(exp)
+ if not num_1:
+ return False
+
+ _, num_2 = ToNum(val)
+ if not num_2:
+ return False
+
+ return num_1 == num_2
diff --git a/extras/packetforge/ForgeResult.py b/extras/packetforge/ForgeResult.py
new file mode 100644
index 00000000000..c8611a70ae3
--- /dev/null
+++ b/extras/packetforge/ForgeResult.py
@@ -0,0 +1,45 @@
+# Copyright (c) 2022 Intel 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.
+
+import ExpressionConverter
+
+
+class ForgeResult:
+ def __init__(self, Header, PacketBuffer, MaskBuffer):
+ self.Headers = Header
+ self.PacketBuffer = PacketBuffer
+ self.MaskBuffer = MaskBuffer
+
+ def ToJSON(self):
+ result = {}
+ result["Length"] = str(len(self.PacketBuffer))
+ result["Packet"] = ExpressionConverter.ByteArrayToString(self.PacketBuffer)
+ result["Mask"] = ExpressionConverter.ByteArrayToString(self.MaskBuffer)
+ result["Protocol Stack"] = []
+
+ for header in self.Headers:
+ head_info = {}
+ head_info["name"] = header.Name()
+ head_info["Fields"] = []
+ for field in header.fields:
+ if field.Size == 0:
+ continue
+ field_info = {}
+ field_info["name"] = field.Field.Name
+ field_info["size"] = str(field.Size)
+ field_info["value"] = field.Value
+ field_info["mask"] = field.Mask
+ head_info["Fields"].append(field_info)
+ result["Protocol Stack"].append(head_info)
+
+ return result
diff --git a/extras/packetforge/InputFormat.py b/extras/packetforge/InputFormat.py
new file mode 100644
index 00000000000..dfba1676934
--- /dev/null
+++ b/extras/packetforge/InputFormat.py
@@ -0,0 +1,12 @@
+import enum
+
+
+class InputFormat(enum.Enum):
+ mac = 0
+ ipv4 = 1
+ ipv6 = 2
+ u8 = 3
+ u16 = 4
+ u32 = 5
+ u64 = 6
+ bytearray = 7
diff --git a/extras/packetforge/LICENSE.txt b/extras/packetforge/LICENSE.txt
new file mode 100644
index 00000000000..8f71f43fee3
--- /dev/null
+++ b/extras/packetforge/LICENSE.txt
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
+
diff --git a/extras/packetforge/Node.py b/extras/packetforge/Node.py
new file mode 100644
index 00000000000..798542a0335
--- /dev/null
+++ b/extras/packetforge/Node.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2022 Intel 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.
+
+from NodeField import *
+from NodeAttribute import *
+import json
+
+
+class Node:
+ def __init__(self):
+ self.fields = []
+ self.attributes = []
+ self.attrsDict = {}
+ self.fieldDict = {}
+
+ def Create(jsonfile):
+ f = open(jsonfile, "r", encoding="utf-8")
+ token = json.load(f)
+
+ if token == None:
+ return None
+
+ if token["type"] != "node":
+ return None
+
+ node = Node()
+
+ name = token["name"]
+ if name == None:
+ return None
+
+ node.Name = name
+
+ if token["layout"] == None:
+ return None
+
+ for ft in token["layout"]:
+ field = NodeField.Create(ft)
+ if field == None:
+ return None
+ node.fields.append(field)
+ if not field.IsReserved:
+ node.fieldDict[field.Name] = field
+
+ if "attributes" in token and token["attributes"] != None:
+ for ft in token["attributes"]:
+ attr = NodeAttribute.Create(ft)
+ node.attrsDict[attr.Name] = attr
+ node.attributes.append(attr)
+
+ node.JSON = jsonfile
+ return node
diff --git a/extras/packetforge/NodeAttribute.py b/extras/packetforge/NodeAttribute.py
new file mode 100644
index 00000000000..007b9e050ad
--- /dev/null
+++ b/extras/packetforge/NodeAttribute.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2022 Intel 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.
+
+from InputFormat import *
+import ExpressionConverter
+
+
+class NodeAttribute:
+ def __init__(self):
+ self.DefaultValue = None
+
+ def Create(token):
+ if token == None:
+ return None
+
+ attr = NodeAttribute()
+
+ if token["name"] == None:
+ return None
+ if token["size"] == None:
+ return None
+
+ # name
+ attr.Name = token["name"]
+
+ inputFormat = InputFormat.bytearray
+ res, u16 = ExpressionConverter.ToNum(token["size"])
+
+ # size
+ if res:
+ attr.Size = u16
+ if u16 <= 8:
+ inputFormat = InputFormat.u8
+ elif u16 <= 16:
+ inputFormat = InputFormat.u16
+ elif u16 <= 32:
+ inputFormat = InputFormat.u32
+ elif u16 <= 64:
+ inputFormat = InputFormat.u64
+ else:
+ inputFormat = InputFormat.bytearray
+ else:
+ return None
+
+ if "format" in token and token["format"] != None:
+ inputFormat = InputFormat[token["format"]]
+
+ attr.Format = inputFormat
+ if "default" in token and token["default"] != None:
+ attr.DefaultValue = token["default"]
+ ret, _ = ExpressionConverter.Verify(attr.Format, attr.DefaultValue)
+ if not ret:
+ return None
+
+ return attr
diff --git a/extras/packetforge/NodeField.py b/extras/packetforge/NodeField.py
new file mode 100644
index 00000000000..5788984630f
--- /dev/null
+++ b/extras/packetforge/NodeField.py
@@ -0,0 +1,86 @@
+# Copyright (c) 2022 Intel 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.
+
+import ExpressionConverter
+from InputFormat import *
+
+
+class NodeField:
+ def __init__(self):
+ self.DefaultValue = None
+ self.IsReserved = None
+ self.IsReadonly = None
+ self.IsAutoIncrease = None
+ self.IsIncreaseLength = None
+ self.Optional = None
+ self.VariableSize = None
+
+ def Create(token):
+ if token == None:
+ return None
+
+ field = NodeField()
+
+ if token["name"] == None:
+ return None
+ if token["size"] == None:
+ return None
+
+ # name
+ field.Name = token["name"]
+
+ if field.Name == "reserved":
+ field.IsReserved = True
+
+ inputFormat = InputFormat.bytearray
+ res, u16 = ExpressionConverter.ToNum(token["size"])
+
+ # size
+ if res:
+ field.Size = u16
+ if u16 <= 8:
+ inputFormat = InputFormat.u8
+ elif u16 <= 16:
+ inputFormat = InputFormat.u16
+ elif u16 <= 32:
+ inputFormat = InputFormat.u32
+ elif u16 <= 64:
+ inputFormat = InputFormat.u64
+ else:
+ inputFormat = InputFormat.bytearray
+ else:
+ field.Size = 0
+ field.VariableSize = token["size"]
+
+ if "format" in token and token["format"] != None:
+ inputFormat = InputFormat[token["format"]]
+
+ field.Format = inputFormat
+
+ if "default" in token and token["default"] != None:
+ field.DefaultValue = token["default"]
+ ret, _ = ExpressionConverter.Verify(field.Format, field.DefaultValue)
+ if not ret:
+ return None
+
+ if "readonly" in token and token["readonly"] == "true" or field.IsReserved:
+ field.IsReadonly = True
+ if "autoincrease" in token and token["autoincrease"] == "true":
+ field.IsAutoIncrease = True
+ field.IsReadonly = True
+ if "increaselength" in token and token["increaselength"] == "true":
+ field.IsIncreaseLength = True
+ if "optional" in token:
+ field.Optional = token["optional"]
+
+ return field
diff --git a/extras/packetforge/ParseGraph.py b/extras/packetforge/ParseGraph.py
new file mode 100644
index 00000000000..31fc3039fed
--- /dev/null
+++ b/extras/packetforge/ParseGraph.py
@@ -0,0 +1,179 @@
+# Copyright (c) 2022 Intel 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.
+
+from ProtocolHeader import *
+from ForgeResult import *
+from Node import *
+from Edge import *
+import os
+
+
+class ParseGraph:
+ def __init__(self):
+ self.nodeDict = {}
+ self.edgeDict = {}
+
+ def Create(folder):
+ try:
+ pg = ParseGraph()
+ if not os.path.exists(folder):
+ print("folder not exisit")
+ return None
+
+ if os.path.exists(folder + "/nodes"):
+ pg.LoadNodesFromDirectory(folder + "/nodes")
+ if os.path.exists(folder + "/edges"):
+ pg.LoadEdgesFromDirectory(folder + "/edges")
+ except:
+ print("Failed to create Parse Graph")
+ return None
+ else:
+ return pg
+
+ def Nodes(self):
+ nodes = []
+ nodes.extend(self.nodeDict.values)
+ return nodes
+
+ def Edges(self):
+ edges = []
+ edges.extend(self.edgeDict.values)
+ return edges
+
+ def LoadNodesFromDirectory(self, folder):
+ for root, dirs, files in os.walk(folder):
+ for f in files:
+ self.LoadNodeFromFile(os.path.join(root, f))
+
+ def LoadEdgesFromDirectory(self, folder):
+ for root, dirs, files in os.walk(folder):
+ for f in files:
+ self.LoadEdgeFromFile(os.path.join(root, f))
+
+ def LoadNodeFromFile(self, file):
+ try:
+ node = Node.Create(file)
+
+ if node == None:
+ print("No node created")
+ return None
+
+ self.AddNode(node)
+ except:
+ print("Failed to create node from " + file)
+
+ def LoadEdgeFromFile(self, file):
+ try:
+ edges = Edge.Create(file)
+
+ if edges == None:
+ print("No edge created")
+ return None
+
+ for edge in edges:
+ self.AddEdge(edge)
+ except:
+ print("Failed to create edge from " + file)
+
+ def createProtocolHeader(self, name):
+ if name in self.nodeDict:
+ return ProtocolHeader(self.nodeDict[name])
+ return None
+
+ def GetNode(self, name):
+ if name in self.nodeDict:
+ return self.nodeDict[name]
+ return None
+
+ def GetEdge(self, start, end):
+ key = start + "-" + end
+ if key in self.edgeDict:
+ return self.edgeDict[key]
+ return None
+
+ def AddNode(self, node):
+ if node.Name in self.nodeDict:
+ print("Warning: node {0} already exist", node.Name)
+
+ self.nodeDict[node.Name] = node
+
+ def AddEdge(self, edge):
+ key = edge.Start + "-" + edge.End
+ if key in self.edgeDict:
+ print("Warning: edge {0} already exist", key)
+ self.edgeDict[key] = edge
+
+ def Forge(self, path):
+ headerList = []
+
+ # set field value/mask
+ for headerConfig in path.stack:
+ header = self.createProtocolHeader(headerConfig.Header)
+
+ if header == None:
+ return None
+
+ for hcf in headerConfig.fields:
+ attr = False
+ if not header.SetField(hcf.Name, hcf.Value):
+ if not header.SetAttribute(hcf.Name, hcf.Value):
+ print("failed to set value of " + hcf.Name)
+ return None
+ else:
+ attr = True
+
+ if not attr and not header.SetMask(hcf.Name, hcf.Mask):
+ print("failed to set mask of " + hcf.Name)
+ return None
+
+ header.Adjust()
+
+ headerList.append(header)
+
+ # apply edge actions and length autoincrease
+ for i in range(1, len(headerList)):
+ start = headerList[i - 1]
+ end = headerList[i]
+
+ edge = self.GetEdge(start.Name(), end.Name())
+
+ if edge == None:
+ print("no edge exist for {0}, {1}", start.Name, end.Name)
+ return None
+
+ edge.Apply(start, end)
+
+ increase = end.GetSize()
+ for j in range(i):
+ headerList[j].AppendAuto(increase)
+
+ # resolve buffer
+ pktLen = 0
+ for header in headerList:
+ header.Resolve()
+ pktLen += len(header.Buffer)
+
+ # join buffer
+ pktbuf = []
+ mskbuf = []
+
+ offset = 0
+ for header in headerList:
+ pktbuf.extend(header.Buffer)
+ mskbuf.extend(header.Mask)
+
+ offset += len(header.Buffer)
+
+ result = ForgeResult(headerList, pktbuf, mskbuf)
+
+ return result
diff --git a/extras/packetforge/Path.py b/extras/packetforge/Path.py
new file mode 100644
index 00000000000..ee80255e0d9
--- /dev/null
+++ b/extras/packetforge/Path.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2022 Intel 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.
+
+from PathNode import *
+
+
+class Path:
+ def __init__(self):
+ self.stack = []
+
+ def Create(token):
+ try:
+ path = Path()
+ ss = token["stack"]
+
+ if ss == None:
+ return None
+
+ for hct in ss:
+ path.stack.append(PathNode.Create(hct))
+
+ return path
+ except:
+ print("Failed to create Path from jsonfile")
+ return None
diff --git a/extras/packetforge/PathNode.py b/extras/packetforge/PathNode.py
new file mode 100644
index 00000000000..4f31a0e1f96
--- /dev/null
+++ b/extras/packetforge/PathNode.py
@@ -0,0 +1,39 @@
+# Copyright (c) 2022 Intel 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.
+
+from PathNodeField import *
+
+
+class PathNode:
+ def __init__(self):
+ self.Header = None
+ self.fields = []
+
+ def Create(token):
+ if token == None:
+ return None
+
+ config = PathNode()
+
+ if "header" in token:
+ config.Header = token["header"]
+ if config.Header == None:
+ return None
+
+ if "fields" in token:
+ fts = token["fields"]
+ if fts != None:
+ for ft in fts:
+ config.fields.append(PathNodeField.Create(ft))
+
+ return config
diff --git a/extras/packetforge/PathNodeField.py b/extras/packetforge/PathNodeField.py
new file mode 100644
index 00000000000..8f5d3bb2adc
--- /dev/null
+++ b/extras/packetforge/PathNodeField.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2022 Intel 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.
+
+
+class PathNodeField:
+ def __init__(self):
+ self.Name = None
+ self.Value = None
+ self.Mask = None
+
+ def Create(token):
+ if token == None:
+ return None
+
+ field = PathNodeField()
+
+ if "name" in token:
+ field.Name = token["name"]
+ if "value" in token:
+ field.Value = token["value"]
+ if "mask" in token:
+ field.Mask = token["mask"]
+
+ if field.Name == None:
+ return None
+
+ return field
diff --git a/extras/packetforge/ProtocolHeader.py b/extras/packetforge/ProtocolHeader.py
new file mode 100644
index 00000000000..398a52d3455
--- /dev/null
+++ b/extras/packetforge/ProtocolHeader.py
@@ -0,0 +1,387 @@
+# Copyright (c) 2022 Intel 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.
+
+from ProtocolHeaderAttribute import *
+from ProtocolHeaderField import *
+from InputFormat import *
+import ExpressionConverter
+import copy
+
+
+class ProtocolHeader:
+ def __init__(self, node):
+ self.fields = []
+ self.attributes = []
+ self.fieldDict = {}
+ self.attributeDict = {}
+ self.Buffer = []
+ self.Mask = []
+
+ self.node = node
+ for field in self.node.fields:
+ phf = ProtocolHeaderField(field.Size, field.DefaultValue, None, field)
+ self.fields.append(phf)
+ if field.Name != "reserved":
+ self.fieldDict[field.Name] = phf
+
+ for attr in self.node.attributes:
+ pha = ProtocolHeaderAttribute(attr.Size, attr.DefaultValue, attr)
+ self.attributes.append(pha)
+ self.attributeDict[attr.Name] = pha
+
+ def Name(self):
+ return self.node.Name
+
+ def Fields(self):
+ return self.fields
+
+ def Attributes(self):
+ return self.attributes
+
+ def setField(self, name, expression, auto):
+ if name == "reserved":
+ return False
+
+ if name not in self.fieldDict:
+ return False
+
+ field = self.fieldDict[name]
+
+ if field.UpdateValue(expression, auto):
+ field.UpdateSize()
+ return True
+
+ return False
+
+ def SetField(self, name, expression):
+ return self.setField(name, expression, False)
+
+ def SetFieldAuto(self, name, expression):
+ return self.setField(name, expression, True)
+
+ def SetAttribute(self, name, expression):
+ if name not in self.attributeDict:
+ return False
+ attr = self.attributeDict[name]
+
+ return attr.UpdateValue(expression)
+
+ def SetMask(self, name, expression):
+ if name not in self.fieldDict:
+ return False
+ field = self.fieldDict[name]
+
+ return field.UpdateMask(expression)
+
+ def resolveOptional(self, condition):
+ if condition == None:
+ return True
+
+ tokens = condition.split("|")
+
+ if len(tokens) > 1:
+ result = False
+
+ for token in tokens:
+ result |= self.resolveOptional(token)
+
+ return result
+
+ tokens = condition.split("&")
+
+ if len(tokens) > 1:
+ result = True
+
+ for token in tokens:
+ result &= self.resolveOptional(token)
+
+ return result
+
+ key = None
+ value = None
+
+ if "!=" in tokens[0]:
+ index = tokens[0].find("!=")
+ key = tokens[0][:index].strip()
+ value = tokens[0][index + 1 :].strip()
+ elif "=" in tokens[0]:
+ index = tokens[0].find("=")
+ key = tokens[0][:index].strip()
+ value = tokens[0][index + 1 :].strip()
+ else:
+ return False
+
+ if key not in self.fieldDict:
+ return False
+
+ f = self.fieldDict[key]
+ return ExpressionConverter.Equal(f.Value, value)
+
+ def resolveSize(self, exp):
+ shift = 0
+ key = exp
+
+ if "<<" in exp:
+ offset = exp.find("<<")
+ key = exp[0:offset].strip()
+ shift = int(exp[offset + 2 :].strip())
+
+ if key in self.fieldDict:
+ field = self.fieldDict[key]
+ _, u16 = ExpressionConverter.ToNum(field.Value)
+ if u16:
+ return u16 << shift
+ else:
+ return 0
+
+ if key in self.attributeDict:
+ attr = self.attributeDict[key]
+ _, u16 = ExpressionConverter.ToNum(attr.Value)
+ if u16:
+ return u16 << shift
+ else:
+ return 0
+
+ return 0
+
+ def Adjust(self):
+ autoIncreases = []
+ increaseHeaders = []
+
+ self.resolveAllSize()
+
+ for phf in self.fields:
+ if phf.Field.IsAutoIncrease:
+ autoIncreases.append(phf)
+ if phf.Field.IsIncreaseLength and self.resolveOptional(phf.Field.Optional):
+ increaseHeaders.append(phf)
+
+ for f1 in autoIncreases:
+ for f2 in increaseHeaders:
+ f1.UpdateValue(
+ ExpressionConverter.IncreaseValue(f1.Value, f2.Size >> 3), True
+ )
+
+ def resolveAllSize(self):
+ for phf in self.fields:
+ if phf.Field.Optional != None and not self.resolveOptional(
+ phf.Field.Optional
+ ):
+ size = 0
+ else:
+ if phf.Field.VariableSize != None:
+ size = self.resolveSize(phf.Field.VariableSize)
+ else:
+ size = phf.Field.Size
+ phf.Size = size
+
+ def GetSize(self):
+ size = 0
+
+ for field in self.fields:
+ size += field.Size
+
+ return size >> 3
+
+ def AppendAuto(self, size):
+ for phf in self.fields:
+ if not phf.Field.IsAutoIncrease:
+ continue
+
+ phf.UpdateValue(ExpressionConverter.IncreaseValue(phf.Value, size), True)
+
+ def getField(self, name):
+ if name not in self.fieldDict:
+ return None
+ field = self.fieldDict[name]
+
+ return field.Value
+
+ def getAttribute(self, name):
+ if name not in self.attributeDict:
+ return None
+
+ return self.attributeDict[name].Value
+
+ def GetValue(self, name):
+ result = self.getField(name)
+
+ if result == None:
+ return self.getAttribute(name)
+
+ return result
+
+ def appendNum(self, big, exp, size):
+ num = 0
+ if exp != None:
+ _, num = ExpressionConverter.ToNum(exp)
+ if num == None:
+ print("Invalid byte expression")
+ return None
+
+ # cut msb
+ num = num & ((1 << size) - 1)
+ big = big << size
+ big = big | num
+ return big
+
+ def appendUInt64(self, big, exp, size):
+ u64 = 0
+ if exp != None:
+ _, u64 = ExpressionConverter.ToNum(exp)
+ if not u64:
+ print("Invalid UInt32 expression")
+ return False
+
+ # cut msb
+ if size < 64:
+ u64 = u64 & ((1 << size) - 1)
+ big = big << size
+ big = big | u64
+ return big
+
+ def appendIPv4(self, big, exp):
+ ipv4 = bytes(4)
+ if exp != None:
+ _, ipv4 = ExpressionConverter.ToIPv4Address(exp)
+ if not ipv4:
+ print("Inavalid IPv4 Address")
+ return False
+
+ for i in range(len(ipv4)):
+ big = big << 8
+ big = big | ipv4[i]
+
+ return big
+
+ def appendIPv6(self, big, exp):
+ ipv6 = bytes(16)
+ if exp != None:
+ _, ipv6 = ExpressionConverter.ToIPv6Address(exp)
+ if not ipv6:
+ print("Inavalid IPv6 Address")
+ return False
+
+ for i in range(16):
+ big = big << 8
+ big = big | ipv6[i]
+
+ return big
+
+ def appendMAC(self, big, exp):
+ mac = bytes(6)
+ if exp != None:
+ _, mac = ExpressionConverter.ToMacAddress(exp)
+ if not mac:
+ print("Inavalid MAC Address")
+ return False
+
+ for i in range(6):
+ big = big << 8
+ big = big | mac[i]
+
+ return big
+
+ def appendByteArray(self, big, exp, size):
+ array = bytes(size >> 3)
+ if exp != None:
+ _, array = ExpressionConverter.ToByteArray(exp)
+ if not array:
+ print("Invalid byte array")
+ return False
+
+ for i in range(size >> 3):
+ big = big << 8
+ if i < len(array):
+ big = big | array[i]
+
+ return big
+
+ def append(self, big, phf):
+ bigVal = big["bigVal"]
+ bigMsk = big["bigMsk"]
+
+ if phf.Field.IsReserved:
+ bigVal <<= phf.Size
+ bigMsk <<= phf.Size
+ big.update(bigVal=bigVal, bigMsk=bigMsk)
+ return big, phf.Size
+
+ size = phf.Size
+
+ if (
+ phf.Field.Format == InputFormat.u8
+ or phf.Field.Format == InputFormat.u16
+ or phf.Field.Format == InputFormat.u32
+ ):
+ bigVal = self.appendNum(bigVal, phf.Value, size)
+ bigMsk = self.appendNum(bigMsk, phf.Mask, size)
+
+ elif phf.Field.Format == InputFormat.u64:
+ bigVal = self.appendUInt64(bigVal, phf.Value, size)
+ bigMsk = self.appendUInt64(bigMsk, phf.Mask, size)
+
+ elif phf.Field.Format == InputFormat.ipv4:
+ bigVal = self.appendIPv4(bigVal, phf.Value)
+ bigMsk = self.appendIPv4(bigMsk, phf.Mask)
+
+ elif phf.Field.Format == InputFormat.ipv6:
+ bigVal = self.appendIPv6(bigVal, phf.Value)
+ bigMsk = self.appendIPv6(bigMsk, phf.Mask)
+
+ elif phf.Field.Format == InputFormat.mac:
+ bigVal = self.appendMAC(bigVal, phf.Value)
+ bigMsk = self.appendMAC(bigMsk, phf.Mask)
+
+ elif phf.Field.Format == InputFormat.bytearray:
+ bigVal = self.appendByteArray(bigVal, phf.Value, size)
+ bigMsk = self.appendByteArray(bigMsk, phf.Mask, size)
+
+ else:
+ print("Invalid input format")
+
+ big.update(bigVal=bigVal, bigMsk=bigMsk)
+ return big, size
+
+ def Resolve(self):
+ big = {"bigVal": 0, "bigMsk": 0}
+ offset = 0
+
+ for phf in self.fields:
+ if phf.Size == 0:
+ continue
+
+ big, bits = self.append(big, phf)
+
+ offset += bits
+
+ byteList1 = []
+ byteList2 = []
+
+ bigVal = big["bigVal"]
+ bigMsk = big["bigMsk"]
+
+ while offset > 0:
+ byteList1.append(bigVal & 0xFF)
+ byteList2.append(bigMsk & 0xFF)
+ bigVal = bigVal >> 8
+ bigMsk = bigMsk >> 8
+ offset -= 8
+
+ byteList1.reverse()
+ byteList2.reverse()
+ buffer = copy.deepcopy(byteList1)
+ mask = copy.deepcopy(byteList2)
+
+ self.Buffer = buffer
+ self.Mask = mask
diff --git a/extras/packetforge/ProtocolHeaderAttribute.py b/extras/packetforge/ProtocolHeaderAttribute.py
new file mode 100644
index 00000000000..fd9ea04b96e
--- /dev/null
+++ b/extras/packetforge/ProtocolHeaderAttribute.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2022 Intel 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.
+
+import ExpressionConverter
+
+
+class ProtocolHeaderAttribute:
+ def __init__(self, Size, Value, Attribute):
+ self.Size = Size
+ self.Value = Value
+ self.Attribute = Attribute
+
+ def UpdateValue(self, expression):
+ ret, expression = ExpressionConverter.Verify(self.Attribute.Format, expression)
+ if not ret:
+ return False
+
+ self.Value = expression
+ return True
diff --git a/extras/packetforge/ProtocolHeaderField.py b/extras/packetforge/ProtocolHeaderField.py
new file mode 100644
index 00000000000..bcb88214931
--- /dev/null
+++ b/extras/packetforge/ProtocolHeaderField.py
@@ -0,0 +1,48 @@
+# Copyright (c) 2022 Intel 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.
+
+import ExpressionConverter
+
+
+class ProtocolHeaderField:
+ def __init__(self, Size, Value, Mask, Field):
+ self.Size = Size
+ self.Value = Value
+ self.Mask = Mask
+ self.Field = Field
+
+ def UpdateValue(self, expression, auto):
+ if self.Field.IsReadonly and not auto:
+ return False
+
+ if expression != None:
+ ret, _ = ExpressionConverter.Verify(self.Field.Format, expression)
+ if not ret:
+ return False
+
+ self.Value = expression
+ return True
+
+ def UpdateMask(self, expression):
+ if expression != None:
+ ret, _ = ExpressionConverter.Verify(self.Field.Format, expression)
+ if not ret:
+ return False
+
+ self.Mask = expression
+ return True
+
+ def UpdateSize(self):
+ if self.Size:
+ return
+ self.Size = self.Field.Size
diff --git a/extras/packetforge/README.rst b/extras/packetforge/README.rst
new file mode 100644
index 00000000000..6d959967af4
--- /dev/null
+++ b/extras/packetforge/README.rst
@@ -0,0 +1,82 @@
+.. _packetforge_doc:
+
+Packetforge for generic flow
+============================
+
+Packetforge is a tool to support generic flow. Since the input format of
+generic flow is hard to read and create, packetforge can help to create
+generic flow rules using a format of naming protocols (like Scapy) or json
+profile. Packetforge is built based on a parsegraph, users can modify the
+graph nodes and edges if needed.
+
+Command examples
+----------------
+
+::
+
+ $ python flow_create.py --add -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
+ -a "redirect-to-queue 3" -i 1
+
+ $ python flow_create.py --add
+ --pattern "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
+ --actions "redirect-to-queue 3" --interface 1
+
+ $ python flow_create.py --del -i 1 -I 0
+
+ $ python flow_create.py --del --interface 1 --flow-index 0
+
+Naming format input. There are two operations, add and delete flow rules.
+For add, it needs three parameters. Pattern is similar to Scapy protocols.
+Actions is the same as vnet/flow command. Interface is the device to which
+we want to add the flow rule. For delete, flow index is the index of the
+flow rule we want to delete. We can get the index number when we add the
+flow or use command to show the existed flow entry in CLI.
+
+::
+
+ $ python flow_create.py --add -f "./flow_rule_examples/mac_ipv4.json" -i 1
+
+ $ python flow_create.py --add --file "./flow_rule_examples/mac_ipv4.json"
+ --interface 1
+
+ $ python flow_create.py --add -f "./flow_rule_examples/mac_ipv4.json"
+ -a "redirect-to-queue 3" -i 1
+
+Json profile format input. This command takes a json profile as parameter.
+In the json profile, there will be protocols and their fields and values.
+Users can define spec and mask for each field. Actions can be added in the
+profile directly, otherwise "-a" option should be added in the command to
+specify actions. The example can be found in parsegraph/samples folder.
+Users can create their own json files according to examples and Spec.
+
+::
+
+ $ python flow_create.py --show -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
+
+ $ python flow_parse.py --show -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
+
+These commands can show the forging result of spec and mask only, without invoving
+VPP. No need to configure actions and interfaces. Users can get the binary string
+of spec and mask from a flow pattern if needed. flow_parse.py can be used without
+VAPI installed.
+
+::
+
+ $ show flow entry
+
+It is a vnet/flow command, used in VPP CLI. It can show the added flow rules
+after using the above commands. Users can get the flow index with this command
+and use it to delete the flow rule.
+
+ParseGraph
+----------
+
+Packetforge is built based on a ParseGraph. The ParseGraph is constructed
+with nodes and edges. Nodes are protocols, including information about
+protocol's name, fields and default values. Edges are the relationship
+between two protocols, including some actions needed when connecting two
+protocols. For example, change the mac header ethertype to 0x0800 when
+connecting mac and ipv4. More details are in the Spec in parsegraph folder.
+Users can build the ParseGraph following the spec by themselves, like
+adding a new protocol. If NIC supports the new protocol, the rule can be
+created. Otherwise, it will return error.
diff --git a/extras/packetforge/StringFormat.py b/extras/packetforge/StringFormat.py
new file mode 100644
index 00000000000..a9fe0905aeb
--- /dev/null
+++ b/extras/packetforge/StringFormat.py
@@ -0,0 +1,11 @@
+import enum
+
+
+class StringFormat(enum.Enum):
+ mac = 0
+ ipv4 = 1
+ ipv6 = 2
+ u8 = 3
+ u16 = 4
+ u32 = 5
+ u64 = 6
diff --git a/extras/packetforge/flow_create.py b/extras/packetforge/flow_create.py
new file mode 100644
index 00000000000..3d4e4b6a702
--- /dev/null
+++ b/extras/packetforge/flow_create.py
@@ -0,0 +1,173 @@
+# Copyright (c) 2022 Intel 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.
+
+from vpp_papi.vpp_papi import VPPApiClient
+import sys, getopt
+import packetforge
+import fnmatch
+import os
+
+# Get VPP json API file directory
+CLIENT_ID = "Vppclient"
+VPP_JSON_DIR = (
+ os.path.abspath("../..") + "/build-root/install-vpp-native/vpp/share/vpp/api/core"
+)
+VPP_JSON_DIR_PLUGIN = (
+ os.path.abspath("../..")
+ + "/build-root/install-vpp-native/vpp/share/vpp/api/plugins"
+)
+API_FILE_SUFFIX = "*.api.json"
+
+
+def load_json_api_files(suffix=API_FILE_SUFFIX):
+ jsonfiles = []
+ json_dir = VPP_JSON_DIR
+ for root, dirnames, filenames in os.walk(json_dir):
+ for filename in fnmatch.filter(filenames, suffix):
+ jsonfiles.append(os.path.join(json_dir, filename))
+ json_dir = VPP_JSON_DIR_PLUGIN
+ for root, dirnames, filenames in os.walk(json_dir):
+ for filename in fnmatch.filter(filenames, suffix):
+ jsonfiles.append(os.path.join(json_dir, filename))
+ if not jsonfiles:
+ raise RuntimeError("Error: no json api files found")
+ else:
+ print("load json api file done")
+ return jsonfiles
+
+
+def connect_vpp(jsonfiles):
+ vpp = VPPApiClient(apifiles=jsonfiles)
+ r = vpp.connect("CLIENT_ID")
+ print("VPP api opened with code: %s" % r)
+ return vpp
+
+
+def Main(argv):
+ file_flag = False
+ operation = None
+ actions = ""
+ iface = ""
+ try:
+ opts, args = getopt.getopt(
+ argv,
+ "hf:p:a:i:I:",
+ [
+ "help",
+ "add",
+ "del",
+ "show",
+ "file=",
+ "pattern=",
+ "actions=",
+ "interface=",
+ "flow-index=",
+ ],
+ )
+ except getopt.GetoptError:
+ print(
+ "flow_create.py --add|del|show -f <file> -p <pattern> -a <actions> -i <interface> -I <flow-index>"
+ )
+ sys.exit()
+ for opt, arg in opts:
+ if opt == "-h":
+ print(
+ "flow_create.py --add|del|show -f <file> -p <pattern> -a <actions> -i <interface> -I <flow-index>"
+ )
+ sys.exit()
+ elif opt == "--add":
+ operation = "add"
+ elif opt == "--del":
+ operation = "del"
+ elif opt == "--show":
+ operation = "show"
+ elif opt in ("-f", "--file"):
+ json_file = arg
+ file_flag = True
+ elif opt in ("-p", "--pattern") and not file_flag:
+ pattern = arg
+ elif opt in ("-a", "--actions"):
+ actions = arg
+ elif opt in ("-i", "--interface"):
+ iface = arg
+ elif opt in ("-I", "--flow-index"):
+ flow_index = arg
+
+ if operation == None:
+ print("Error: Please choose the operation: add or del")
+ sys.exit()
+
+ if operation == "show":
+ if not file_flag:
+ result = packetforge.Forge(pattern, actions, False, True)
+ else:
+ result = packetforge.Forge(json_file, actions, True, True)
+ return result, None, operation, None, None
+
+ # Python API need json definitions to interpret messages
+ vpp = connect_vpp(load_json_api_files())
+
+ # set inteface states
+ vpp.api.sw_interface_set_flags(sw_if_index=int(iface), flags=1)
+
+ if operation == "add":
+ if not file_flag:
+ result = packetforge.Forge(pattern, actions, False, False)
+ else:
+ result = packetforge.Forge(json_file, actions, True, False)
+ return result, int(iface), operation, None, vpp
+ elif operation == "del":
+ return None, int(iface), operation, int(flow_index), vpp
+
+
+if __name__ == "__main__":
+ # Parse the arguments
+ my_flow, iface, operation, del_flow_index, vpp = Main(sys.argv[1:])
+
+ # if operation is show, just show spec and mask, then exit
+ if operation == "show":
+ print(my_flow)
+ sys.exit()
+
+ if operation == "add":
+ # add flow
+ rv = vpp.api.flow_add_v2(flow=my_flow)
+ flow_index = rv[3]
+ print(rv)
+
+ # enable added flow
+ rv = vpp.api.flow_enable(flow_index=flow_index, hw_if_index=iface)
+ ena_res = rv[2]
+ # if enable flow fail, delete added flow
+ if ena_res:
+ print("Error: enable flow failed, delete flow")
+ rv = vpp.api.flow_del(flow_index=flow_index)
+ print(rv)
+
+ elif operation == "del":
+ # disable flow
+ rv = vpp.api.flow_disable(flow_index=del_flow_index, hw_if_index=iface)
+ dis_res = rv[2]
+ if dis_res:
+ print("Error: disable flow failed")
+ sys.exit()
+ print(rv)
+
+ # delete flow
+ rv = vpp.api.flow_del(flow_index=del_flow_index)
+ print(rv)
+
+# command example:
+# python flow_create.py --add -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()" -a "redirect-to-queue 3" -i 1
+# python flow_create.py --del -i 1 -I 0
+# python flow_create.py --show -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
diff --git a/extras/packetforge/flow_parse.py b/extras/packetforge/flow_parse.py
new file mode 100644
index 00000000000..9b89282e153
--- /dev/null
+++ b/extras/packetforge/flow_parse.py
@@ -0,0 +1,66 @@
+# Copyright (c) 2022 Intel 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.
+
+import sys, getopt
+import packetforge
+
+
+def Main(argv):
+ file_flag = False
+ operation = None
+ try:
+ opts, args = getopt.getopt(
+ argv,
+ "hf:p:a:i:I:",
+ [
+ "help",
+ "show",
+ "file=",
+ "pattern=",
+ ],
+ )
+ except getopt.GetoptError:
+ print("flow_parse.py --show -f <file> -p <pattern>")
+ sys.exit()
+ for opt, arg in opts:
+ if opt == "-h":
+ print("flow_parse.py --show -f <file> -p <pattern>")
+ sys.exit()
+ elif opt == "--show":
+ operation = "show"
+ elif opt in ("-f", "--file"):
+ json_file = arg
+ file_flag = True
+ elif opt in ("-p", "--pattern") and not file_flag:
+ pattern = arg
+
+ if operation == None:
+ print("Error: Please choose the operation: show")
+ sys.exit()
+
+ if not file_flag:
+ result = packetforge.Forge(pattern, None, False, True)
+ else:
+ result = packetforge.Forge(json_file, None, True, True)
+
+ return result
+
+
+if __name__ == "__main__":
+ # Parse the arguments
+ my_flow = Main(sys.argv[1:])
+
+ print(my_flow)
+
+# command example:
+# python flow_parse.py --show -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
diff --git a/extras/packetforge/packetforge.py b/extras/packetforge/packetforge.py
new file mode 100644
index 00000000000..42bfeb01e00
--- /dev/null
+++ b/extras/packetforge/packetforge.py
@@ -0,0 +1,208 @@
+# Copyright (c) 2022 Intel 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.
+
+from vpp_papi.vpp_papi import VppEnum
+from ParseGraph import *
+from Path import *
+import json
+import re
+import os
+
+parsegraph_path = os.getcwd() + "/parsegraph"
+
+
+def Forge(pattern, actions, file_flag, show_result_only):
+ pg = ParseGraph.Create(parsegraph_path)
+ if pg == None:
+ print("error: create parsegraph failed")
+ return None
+
+ if not file_flag:
+ token = ParsePattern(pattern)
+ if token == None:
+ return None
+ else:
+ if not os.path.exists(pattern):
+ print("error: file not exist '%s' " % (pattern))
+ return
+ f = open(pattern, "r", encoding="utf-8")
+ token = json.load(f)
+ if "actions" in token:
+ actions = token["actions"]
+
+ path = Path.Create(token)
+ if path == None:
+ print("error: path not exit")
+ return None
+
+ result = pg.Forge(path)
+ if result == None:
+ print("error: result not available")
+ return None
+
+ spec, mask = GetBinary(result.ToJSON())
+
+ # create generic flow
+ my_flow = {
+ "flow": {
+ "generic": {
+ "pattern": {"spec": bytes(spec.encode()), "mask": bytes(mask.encode())}
+ }
+ },
+ }
+
+ if show_result_only:
+ return my_flow
+
+ my_flow.update(
+ {
+ "type": VppEnum.vl_api_flow_type_v2_t.FLOW_TYPE_GENERIC_V2,
+ }
+ )
+
+ # update actions entry
+ my_flow = GetAction(actions, my_flow)
+
+ return my_flow
+
+
+def GetAction(actions, flow):
+ if len(actions.split(" ")) > 1:
+ type = actions.split(" ")[0]
+ else:
+ type = actions
+
+ if type == "mark":
+ flow.update(
+ {
+ "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_MARK_V2,
+ "mark_flow_id": int(actions.split(" ")[1]),
+ }
+ )
+ elif type == "next-node":
+ flow.update(
+ {
+ "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_REDIRECT_TO_NODE_V2,
+ "redirect_node_index": int(actions.split(" ")[1]),
+ }
+ )
+ elif type == "buffer-advance":
+ flow.update(
+ {
+ "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_BUFFER_ADVANCE_V2,
+ "buffer_advance": int(actions.split(" ")[1]),
+ }
+ )
+ elif type == "redirect-to-queue":
+ flow.update(
+ {
+ "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_REDIRECT_TO_QUEUE_V2,
+ "redirect_queue": int(actions.split(" ")[1]),
+ }
+ )
+ elif type == "rss":
+ flow.update({"actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_RSS_V2})
+ elif type == "rss-queues":
+ queue_end = int(actions.split(" ")[-1])
+ queue_start = int(actions.split(" ")[-3])
+ flow.update(
+ {
+ "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_RSS_V2,
+ "queue_index": queue_start,
+ "queue_num": queue_end - queue_start + 1,
+ }
+ )
+ elif type == "drop":
+ flow.update({"actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_DROP_V2})
+
+ return flow
+
+
+def GetBinary(flow_info):
+ spec = "".join(flow_info["Packet"])
+ mask = "".join(flow_info["Mask"])
+ return spec, mask
+
+
+def ParseFields(item):
+ # get protocol name
+ prot = item.split("(")[0]
+ stack = {"header": prot}
+ # get fields contents
+ fields = re.findall(r"[(](.*?)[)]", item)
+ if not fields:
+ print("error: invalid pattern")
+ return None
+ if fields == [""]:
+ return stack
+ stack.update({"fields": []})
+ return ParseStack(stack, fields[0].split(","))
+
+
+def GetMask(item):
+ if "format" in item:
+ format = item["format"]
+ if format == "mac":
+ mask = "ff:ff:ff:ff:ff:ff"
+ elif format == "ipv4":
+ mask = "255.255.255.255"
+ elif format == "ipv6":
+ mask = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+ return mask
+ if "size" in item:
+ mask = str((1 << int(item["size"])) - 1)
+ else:
+ print("mask error")
+ return mask
+
+
+# parse protocol headers and its fields. Available fields are defined in corresponding nodes.
+def ParseStack(stack, fields):
+ prot = stack["header"]
+ node_path = parsegraph_path + "/nodes/" + prot + ".json"
+ if not os.path.exists(node_path):
+ print("error file not exist '%s' " % (node_path))
+ return None
+ f = open(node_path, "r", encoding="utf-8")
+ nodeinfo = json.load(f)
+ for field in fields:
+ fld_name = field.split("=")[0].strip()
+ fld_value = (
+ field.split("=")[-1].strip() if (len(field.split("=")) >= 2) else None
+ )
+ for item in nodeinfo["layout"]:
+ if fld_name == item["name"]:
+ mask = GetMask(item)
+ stack["fields"].append(
+ {"name": fld_name, "value": fld_value, "mask": mask}
+ )
+ break
+ if not stack["fields"]:
+ print("warning: invalid field '%s'" % (fld_name))
+ return None
+
+ return stack
+
+
+def ParsePattern(pattern):
+ # create json template
+ json_tmp = {"type": "path", "stack": []}
+
+ items = pattern.split("/")
+ for item in items:
+ stack = ParseFields(item)
+ if stack == None:
+ return None
+ json_tmp["stack"].append(stack)
+
+ return json_tmp
diff --git a/extras/packetforge/parsegraph/.gitattributes b/extras/packetforge/parsegraph/.gitattributes
new file mode 100644
index 00000000000..dfe0770424b
--- /dev/null
+++ b/extras/packetforge/parsegraph/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/extras/packetforge/parsegraph/edges/ah_after_ipv4.json b/extras/packetforge/parsegraph/edges/ah_after_ipv4.json
new file mode 100644
index 00000000000..7f123916114
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ah_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "ah",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "51"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ah_after_ipv6.json b/extras/packetforge/parsegraph/edges/ah_after_ipv6.json
new file mode 100644
index 00000000000..64620fa276b
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ah_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "ah",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "51"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/arpv4_after_macvlan.json b/extras/packetforge/parsegraph/edges/arpv4_after_macvlan.json
new file mode 100644
index 00000000000..28c042b9c93
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/arpv4_after_macvlan.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "mac,vlan",
+ "end" : "arpv4",
+ "actions" : [
+ {
+ "dst" : "start.ethertype",
+ "src" : "0x0806"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/esp_after_ipv4.json b/extras/packetforge/parsegraph/edges/esp_after_ipv4.json
new file mode 100644
index 00000000000..c573ee3aa28
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/esp_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "esp",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "50"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/esp_after_ipv6.json b/extras/packetforge/parsegraph/edges/esp_after_ipv6.json
new file mode 100644
index 00000000000..e3d8bf660d3
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/esp_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "esp",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "50"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/gre_after_ipv4.json b/extras/packetforge/parsegraph/edges/gre_after_ipv4.json
new file mode 100644
index 00000000000..5ae8eb27681
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/gre_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "gre,nvgre",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "47"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/gre_after_ipv6.json b/extras/packetforge/parsegraph/edges/gre_after_ipv6.json
new file mode 100644
index 00000000000..a5a53139de3
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/gre_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "gre,nvgre",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "47"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/gtpc_after_udp.json b/extras/packetforge/parsegraph/edges/gtpc_after_udp.json
new file mode 100644
index 00000000000..237704c87cc
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/gtpc_after_udp.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "udp",
+ "end" : "gtpc",
+ "actions" : [
+ {
+ "dst" : "start.dst",
+ "src" : "2123"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/gtppsc_after_gtpu.json b/extras/packetforge/parsegraph/edges/gtppsc_after_gtpu.json
new file mode 100644
index 00000000000..ec985bb888f
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/gtppsc_after_gtpu.json
@@ -0,0 +1,19 @@
+{
+ "type" : "edge",
+ "start" : "gtpu",
+ "end" : "gtppsc",
+ "actions" : [
+ {
+ "dst" : "start.e",
+ "src" : "1"
+ },
+ {
+ "dst" : "start.nextextentionheadertype",
+ "src" : "0x85"
+ },
+ {
+ "dst" : "start.messagetype",
+ "src" : "0xff"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/gtpu_after_udp.json b/extras/packetforge/parsegraph/edges/gtpu_after_udp.json
new file mode 100644
index 00000000000..e2591fad5ac
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/gtpu_after_udp.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "udp",
+ "end" : "gtpu",
+ "actions" : [
+ {
+ "dst" : "start.dst",
+ "src" : "2152"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/icmp_after_ipv4.json b/extras/packetforge/parsegraph/edges/icmp_after_ipv4.json
new file mode 100644
index 00000000000..e701cc8c28c
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/icmp_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "icmp",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "1"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/icmpv6_after_ipv6.json b/extras/packetforge/parsegraph/edges/icmpv6_after_ipv6.json
new file mode 100644
index 00000000000..a83181450ad
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/icmpv6_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "icmpv6",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "58"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ip_after_gtppsc.json b/extras/packetforge/parsegraph/edges/ip_after_gtppsc.json
new file mode 100644
index 00000000000..13e11a3c67a
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ip_after_gtppsc.json
@@ -0,0 +1,5 @@
+{
+ "type" : "edge",
+ "start" : "gtppsc",
+ "end" : "ipv4"
+}
diff --git a/extras/packetforge/parsegraph/edges/ip_after_gtpu.json b/extras/packetforge/parsegraph/edges/ip_after_gtpu.json
new file mode 100644
index 00000000000..03d89ffc4b9
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ip_after_gtpu.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "gtpu",
+ "end" : "ipv4,ipv6",
+ "actions" : [
+ {
+ "dst" : "start.messagetype",
+ "src" : "0xff"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv4_after_geneve.json b/extras/packetforge/parsegraph/edges/ipv4_after_geneve.json
new file mode 100644
index 00000000000..40c733b9688
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv4_after_geneve.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "geneve",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.protocoltype",
+ "src" : "0x0800"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv4_after_gre.json b/extras/packetforge/parsegraph/edges/ipv4_after_gre.json
new file mode 100644
index 00000000000..87cd14beecd
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv4_after_gre.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "gre",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.protocoltype",
+ "src" : "0x0800"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv4_after_ipv4.json b/extras/packetforge/parsegraph/edges/ipv4_after_ipv4.json
new file mode 100644
index 00000000000..0590f6738b3
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv4_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "4"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv4_after_ipv6.json b/extras/packetforge/parsegraph/edges/ipv4_after_ipv6.json
new file mode 100644
index 00000000000..e804139cfe5
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv4_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "4"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv4_after_macvlan.json b/extras/packetforge/parsegraph/edges/ipv4_after_macvlan.json
new file mode 100644
index 00000000000..3722126f6f4
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv4_after_macvlan.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "mac,vlan",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.ethertype",
+ "src" : "0x0800"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv4_after_vxlangpe.json b/extras/packetforge/parsegraph/edges/ipv4_after_vxlangpe.json
new file mode 100644
index 00000000000..ab7c16707fb
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv4_after_vxlangpe.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "vxlangpe",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.nextprotocol",
+ "src" : "1"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_geneve.json b/extras/packetforge/parsegraph/edges/ipv6_after_geneve.json
new file mode 100644
index 00000000000..6ad81a4550a
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_geneve.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "geneve",
+ "end" : "ipv6",
+ "actions" : [
+ {
+ "dst" : "start.protocoltype",
+ "src" : "0x86dd"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_gre.json b/extras/packetforge/parsegraph/edges/ipv6_after_gre.json
new file mode 100644
index 00000000000..90c40ec3974
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_gre.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "gre",
+ "end" : "ipv6",
+ "actions" : [
+ {
+ "dst" : "start.protocoltype",
+ "src" : "0x86dd"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_gtppsc.json b/extras/packetforge/parsegraph/edges/ipv6_after_gtppsc.json
new file mode 100644
index 00000000000..a0bb99c5bf2
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_gtppsc.json
@@ -0,0 +1,5 @@
+{
+ "type" : "edge",
+ "start" : "gtppsc",
+ "end" : "ipv6"
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_ipv4.json b/extras/packetforge/parsegraph/edges/ipv6_after_ipv4.json
new file mode 100644
index 00000000000..cc57b44cf25
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "ipv6",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "41"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_ipv6.json b/extras/packetforge/parsegraph/edges/ipv6_after_ipv6.json
new file mode 100644
index 00000000000..79184bb55dd
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "ipv6",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "41"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_macvlan.json b/extras/packetforge/parsegraph/edges/ipv6_after_macvlan.json
new file mode 100644
index 00000000000..6da21082024
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_macvlan.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "mac,vlan",
+ "end" : "ipv6",
+ "actions" : [
+ {
+ "dst" : "start.ethertype",
+ "src" : "0x86dd"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6_after_vxlangpe.json b/extras/packetforge/parsegraph/edges/ipv6_after_vxlangpe.json
new file mode 100644
index 00000000000..ebd294cf378
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6_after_vxlangpe.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "vxlangpe",
+ "end" : "ipv6",
+ "actions" : [
+ {
+ "dst" : "start.nextprotocol",
+ "src" : "2"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/ipv6srh_after_ipv6.json b/extras/packetforge/parsegraph/edges/ipv6srh_after_ipv6.json
new file mode 100644
index 00000000000..9ddea00e926
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/ipv6srh_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "ipv6srh",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "43"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/l2tpv2_after_udp.json b/extras/packetforge/parsegraph/edges/l2tpv2_after_udp.json
new file mode 100644
index 00000000000..c34927ac47f
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/l2tpv2_after_udp.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "udp",
+ "end" : "l2tpv2ctl,l2tpv2data",
+ "actions" : [
+ {
+ "dst" : "start.dst",
+ "src" : "1701"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/mac_after_geneve.json b/extras/packetforge/parsegraph/edges/mac_after_geneve.json
new file mode 100644
index 00000000000..3eba08ee802
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/mac_after_geneve.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "geneve",
+ "end" : "mac",
+ "actions" : [
+ {
+ "dst" : "start.protocoltype",
+ "src" : "0x6558"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/mac_after_gre.json b/extras/packetforge/parsegraph/edges/mac_after_gre.json
new file mode 100644
index 00000000000..cb5fd4078e5
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/mac_after_gre.json
@@ -0,0 +1,5 @@
+{
+ "type" : "edge",
+ "start" : "gre",
+ "end" : "mac"
+}
diff --git a/extras/packetforge/parsegraph/edges/mac_after_nvgre.json b/extras/packetforge/parsegraph/edges/mac_after_nvgre.json
new file mode 100644
index 00000000000..1c9f98373bf
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/mac_after_nvgre.json
@@ -0,0 +1,5 @@
+{
+ "type" : "edge",
+ "start" : "nvgre",
+ "end" : "mac"
+}
diff --git a/extras/packetforge/parsegraph/edges/mac_after_vxlan.json b/extras/packetforge/parsegraph/edges/mac_after_vxlan.json
new file mode 100644
index 00000000000..c1c1fa25540
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/mac_after_vxlan.json
@@ -0,0 +1,5 @@
+{
+ "type" : "edge",
+ "start" : "vxlan",
+ "end" : "mac"
+}
diff --git a/extras/packetforge/parsegraph/edges/mac_after_vxlangpe.json b/extras/packetforge/parsegraph/edges/mac_after_vxlangpe.json
new file mode 100644
index 00000000000..b5012bf71fb
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/mac_after_vxlangpe.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "vxlangpe",
+ "end" : "mac",
+ "actions" : [
+ {
+ "dst" : "start.nextprotocol",
+ "src" : "3"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/pfcp_after_udp.json b/extras/packetforge/parsegraph/edges/pfcp_after_udp.json
new file mode 100644
index 00000000000..fec4aaa739e
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/pfcp_after_udp.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "udp",
+ "end" : "pfcp",
+ "actions" : [
+ {
+ "dst" : "start.dst",
+ "src" : "8805"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/sctp_after_ipv4.json b/extras/packetforge/parsegraph/edges/sctp_after_ipv4.json
new file mode 100644
index 00000000000..e58c36efc7a
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/sctp_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "sctp",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "132"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/sctp_after_ipv6.json b/extras/packetforge/parsegraph/edges/sctp_after_ipv6.json
new file mode 100644
index 00000000000..7fa306811eb
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/sctp_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6",
+ "end" : "sctp",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "132"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/tcp_after_ipv4.json b/extras/packetforge/parsegraph/edges/tcp_after_ipv4.json
new file mode 100644
index 00000000000..13c74f85edc
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/tcp_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "tcp",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "6"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/tcp_after_ipv6.json b/extras/packetforge/parsegraph/edges/tcp_after_ipv6.json
new file mode 100644
index 00000000000..f0d0b415e22
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/tcp_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "tcp",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "6"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/tunnel_after_udp.json b/extras/packetforge/parsegraph/edges/tunnel_after_udp.json
new file mode 100644
index 00000000000..fa498d4378e
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/tunnel_after_udp.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "udp",
+ "end" : "geneve,vxlan,vxlangpe",
+ "actions" : [
+ {
+ "dst" : "start.dst",
+ "src" : "end.udpport"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/udp_after_ipv4.json b/extras/packetforge/parsegraph/edges/udp_after_ipv4.json
new file mode 100644
index 00000000000..cb2f533bd39
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/udp_after_ipv4.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "udp",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "17"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/udp_after_ipv6.json b/extras/packetforge/parsegraph/edges/udp_after_ipv6.json
new file mode 100644
index 00000000000..fbbe972e4e5
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/udp_after_ipv6.json
@@ -0,0 +1,11 @@
+{
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "udp",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "17"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/edges/vlan_after_macvlan.json b/extras/packetforge/parsegraph/edges/vlan_after_macvlan.json
new file mode 100644
index 00000000000..5c0b81ebacf
--- /dev/null
+++ b/extras/packetforge/parsegraph/edges/vlan_after_macvlan.json
@@ -0,0 +1,15 @@
+{
+ "type" : "edge",
+ "start" : "mac,vlan",
+ "end" : "vlan",
+ "actions" : [
+ {
+ "dst" : "start.ethertype",
+ "src" : "end.tpid"
+ },
+ {
+ "dst" : "end.ethertype",
+ "src" : "start.ethertype"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/ah.json b/extras/packetforge/parsegraph/nodes/ah.json
new file mode 100644
index 00000000000..b3a32272f94
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/ah.json
@@ -0,0 +1,26 @@
+{
+ "type" : "node",
+ "name" : "ah",
+ "layout" : [
+ {
+ "name" : "nextheader",
+ "size" : "8"
+ },
+ {
+ "name" : "payloadlength",
+ "size" : "8"
+ },
+ {
+ "name" : "reserved",
+ "size" : "16"
+ },
+ {
+ "name" : "spi",
+ "size" : "32"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "32"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/arpv4.json b/extras/packetforge/parsegraph/nodes/arpv4.json
new file mode 100644
index 00000000000..95c72c2b657
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/arpv4.json
@@ -0,0 +1,54 @@
+{
+ "type" : "node",
+ "name" : "arpv4",
+ "layout" : [
+ {
+ "name" : "htype",
+ "size" : "16",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "ptype",
+ "size" : "16",
+ "default" : "0x0800",
+ "readonly" : "true"
+ },
+ {
+ "name" : "hlen",
+ "size" : "8",
+ "default" : "6",
+ "readonly" : "true"
+ },
+ {
+ "name" : "plen",
+ "size" : "8",
+ "default" : "4",
+ "readonly" : "true"
+ },
+ {
+ "name" : "operation",
+ "size" : "16"
+ },
+ {
+ "name" : "sha",
+ "size" : "48",
+ "format" : "mac"
+ },
+ {
+ "name" : "spa",
+ "size" : "32",
+ "format" : "ipv4"
+ },
+ {
+ "name" : "tha",
+ "size" : "48",
+ "format" : "mac"
+ },
+ {
+ "name" : "tpa",
+ "size" : "32",
+ "format" : "ipv4"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/esp.json b/extras/packetforge/parsegraph/nodes/esp.json
new file mode 100644
index 00000000000..702408ab379
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/esp.json
@@ -0,0 +1,14 @@
+{
+ "type" : "node",
+ "name" : "esp",
+ "layout" : [
+ {
+ "name" : "spi",
+ "size" : "32"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "32"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/geneve.json b/extras/packetforge/parsegraph/nodes/geneve.json
new file mode 100644
index 00000000000..c31586b8f7a
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/geneve.json
@@ -0,0 +1,51 @@
+{
+ "type" : "node",
+ "name" : "geneve",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "2",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "optlen",
+ "size" : "6"
+ },
+ {
+ "name" : "o",
+ "size" : "1"
+ },
+ {
+ "name" : "c",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "6"
+ },
+ {
+ "name" : "protocoltype",
+ "size" : "16"
+ },
+ {
+ "name" : "vni",
+ "size" : "24"
+ },
+ {
+ "name" : "reserved",
+ "size" : "8"
+ },
+ {
+ "name" : "options",
+ "size" : "optlen<<5"
+ }
+ ],
+ "attributes" : [
+ {
+ "name" : "udpport",
+ "size" : "16",
+ "default" : "6081"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/gre.json b/extras/packetforge/parsegraph/nodes/gre.json
new file mode 100644
index 00000000000..db9579cd27f
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/gre.json
@@ -0,0 +1,56 @@
+{
+ "type" : "node",
+ "name" : "gre",
+ "layout" : [
+ {
+ "name" : "c",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "1"
+ },
+ {
+ "name" : "k",
+ "size" : "1"
+ },
+ {
+ "name" : "s",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "9"
+ },
+ {
+ "name" : "version",
+ "size" : "3",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "protocoltype",
+ "size" : "16"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16",
+ "optional" : "c=1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "16",
+ "optional" : "c=1"
+ },
+ {
+ "name" : "key",
+ "size" : "32",
+ "optional" : "k=1"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "32",
+ "optional" : "s=1"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/gtpc.json b/extras/packetforge/parsegraph/nodes/gtpc.json
new file mode 100644
index 00000000000..99ed70c06e8
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/gtpc.json
@@ -0,0 +1,48 @@
+{
+ "type" : "node",
+ "name" : "gtpc",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "3",
+ "default" : "2",
+ "readonly" : "true"
+ },
+ {
+ "name" : "p",
+ "size" : "1"
+ },
+ {
+ "name" : "t",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "3"
+ },
+ {
+ "name" : "messagetype",
+ "size" : "8"
+ },
+ {
+ "name" : "messagelength",
+ "size" : "16",
+ "default" : "4",
+ "autoincrease" : "true"
+ },
+ {
+ "name" : "teid",
+ "size" : "32",
+ "optional" : "t=1",
+ "increaselength" : "true"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "24"
+ },
+ {
+ "name" : "reserved",
+ "size" : "8"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/gtppsc.json b/extras/packetforge/parsegraph/nodes/gtppsc.json
new file mode 100644
index 00000000000..bf0ac11376f
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/gtppsc.json
@@ -0,0 +1,32 @@
+{
+ "type" : "node",
+ "name" : "gtppsc",
+ "layout" : [
+ {
+ "name" : "length",
+ "size" : "8",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "pdutype",
+ "size" : "4"
+ },
+ {
+ "name" : "reserved",
+ "size" : "4"
+ },
+ {
+ "name" : "reserved",
+ "size" : "2"
+ },
+ {
+ "name" : "qfi",
+ "size" : "6"
+ },
+ {
+ "name" : "nextext",
+ "size" : "8"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/gtpu.json b/extras/packetforge/parsegraph/nodes/gtpu.json
new file mode 100644
index 00000000000..244e7d9ced0
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/gtpu.json
@@ -0,0 +1,63 @@
+{
+ "type" : "node",
+ "name" : "gtpu",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "3",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "pt",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "1"
+ },
+ {
+ "name" : "e",
+ "size" : "1"
+ },
+ {
+ "name" : "s",
+ "size" : "1"
+ },
+ {
+ "name" : "pn",
+ "size" : "1"
+ },
+ {
+ "name" : "messagetype",
+ "size" : "8"
+ },
+ {
+ "name" : "messagelength",
+ "size" : "16",
+ "autoincrease" : "true"
+ },
+ {
+ "name" : "teid",
+ "size" : "32"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "16",
+ "optional" : "e=1|s=1|pn=1",
+ "increaselength" : "true"
+ },
+ {
+ "name" : "npdunumber",
+ "size" : "8",
+ "optional" : "e=1|s=1|pn=1",
+ "increaselength" : "true"
+ },
+ {
+ "name" : "nextextentionheadertype",
+ "size" : "8",
+ "optional" : "e=1|s=1|pn=1",
+ "increaselength" : "true"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/icmp.json b/extras/packetforge/parsegraph/nodes/icmp.json
new file mode 100644
index 00000000000..4d96a34bd7e
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/icmp.json
@@ -0,0 +1,18 @@
+{
+ "type" : "node",
+ "name" : "icmp",
+ "layout" : [
+ {
+ "name" : "type",
+ "size" : "8"
+ },
+ {
+ "name" : "code",
+ "size" : "8"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/icmpv6.json b/extras/packetforge/parsegraph/nodes/icmpv6.json
new file mode 100644
index 00000000000..c4e89cbae81
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/icmpv6.json
@@ -0,0 +1,18 @@
+{
+ "type" : "node",
+ "name" : "icmpv6",
+ "layout" : [
+ {
+ "name" : "type",
+ "size" : "8"
+ },
+ {
+ "name" : "code",
+ "size" : "8"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/ipv4.json b/extras/packetforge/parsegraph/nodes/ipv4.json
new file mode 100644
index 00000000000..297f60000cc
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/ipv4.json
@@ -0,0 +1,76 @@
+{
+ "type" : "node",
+ "name" : "ipv4",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "4",
+ "default" : "4",
+ "readonly" : "true"
+ },
+ {
+ "name" : "ilh",
+ "size" : "4",
+ "default" : "5",
+ "readonly" : "true"
+ },
+ {
+ "name" : "dscp",
+ "size" : "6"
+ },
+ {
+ "name" : "ecn",
+ "size" : "2"
+ },
+ {
+ "name" : "totallength",
+ "size" : "16",
+ "default" : "20",
+ "autoincrease" : "true"
+ },
+ {
+ "name" : "identification",
+ "size" : "16"
+ },
+ {
+ "name" : "reserved",
+ "size" : "1"
+ },
+ {
+ "name" : "df",
+ "size" : "1"
+ },
+ {
+ "name" : "mf",
+ "size" : "1"
+ },
+ {
+ "name" : "fragmentoffset",
+ "size" : "13"
+ },
+ {
+ "name" : "ttl",
+ "size" : "8"
+ },
+ {
+ "name" : "protocol",
+ "size" : "8"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16"
+ },
+ {
+ "name" : "src",
+ "size" : "32",
+ "format" : "ipv4",
+ "default" : "1.1.1.1"
+ },
+ {
+ "name" : "dst",
+ "size" : "32",
+ "format" : "ipv4",
+ "default" : "2.2.2.2"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/ipv6.json b/extras/packetforge/parsegraph/nodes/ipv6.json
new file mode 100644
index 00000000000..3de24a2d64b
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/ipv6.json
@@ -0,0 +1,47 @@
+{
+ "type" : "node",
+ "name" : "ipv6",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "4",
+ "default" : "6",
+ "readonly" : "true"
+ },
+ {
+ "name" : "dscp",
+ "size" : "6"
+ },
+ {
+ "name" : "ecn",
+ "size" : "2"
+ },
+ {
+ "name" : "flowlabel",
+ "size" : "20"
+ },
+ {
+ "name" : "payloadlength",
+ "size" : "16",
+ "autoincrease" : "true"
+ },
+ {
+ "name" : "nextheader",
+ "size" : "8"
+ },
+ {
+ "name" : "hoplimit",
+ "size" : "8"
+ },
+ {
+ "name" : "src",
+ "size" : "128",
+ "format" : "ipv6"
+ },
+ {
+ "name" : "dst",
+ "size" : "128",
+ "format" : "ipv6"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/ipv6crh16.json b/extras/packetforge/parsegraph/nodes/ipv6crh16.json
new file mode 100644
index 00000000000..559ed870af2
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/ipv6crh16.json
@@ -0,0 +1,36 @@
+{
+ "type" : "node",
+ "name" : "ipv6crh16",
+ "layout" : [
+ {
+ "name" : "nextheader",
+ "size" : "8"
+ },
+ {
+ "name" : "headerextlength",
+ "size" : "8"
+ },
+ {
+ "name" : "routingtype",
+ "size" : "8",
+ "default" : "5",
+ "readonly" : "true"
+ },
+ {
+ "name" : "segmentleft",
+ "size" : "8"
+ },
+ {
+ "name" : "sid0",
+ "size" : "16"
+ },
+ {
+ "name" : "sid1",
+ "size" : "16"
+ },
+ {
+ "name" : "sid16remains",
+ "size" : "headerextlength<<6"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/ipv6crh32.json b/extras/packetforge/parsegraph/nodes/ipv6crh32.json
new file mode 100644
index 00000000000..457050b9f13
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/ipv6crh32.json
@@ -0,0 +1,32 @@
+{
+ "type" : "node",
+ "name" : "ipv6crh32",
+ "layout" : [
+ {
+ "name" : "nextheader",
+ "size" : "8"
+ },
+ {
+ "name" : "headerextlength",
+ "size" : "8"
+ },
+ {
+ "name" : "routingtype",
+ "size" : "8",
+ "default" : "6",
+ "readonly" : "true"
+ },
+ {
+ "name" : "segmentleft",
+ "size" : "8"
+ },
+ {
+ "name" : "sid0",
+ "size" : "32"
+ },
+ {
+ "name" : "sid32remains",
+ "size" : "headerextlength<<6"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/ipv6srh.json b/extras/packetforge/parsegraph/nodes/ipv6srh.json
new file mode 100644
index 00000000000..48feaeb45fa
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/ipv6srh.json
@@ -0,0 +1,40 @@
+{
+ "type" : "node",
+ "name" : "ipv6srh",
+ "layout" : [
+ {
+ "name" : "nextheader",
+ "size" : "8"
+ },
+ {
+ "name" : "headerextlength",
+ "size" : "8"
+ },
+ {
+ "name" : "routingtype",
+ "size" : "8",
+ "default" : "4",
+ "readonly" : "true"
+ },
+ {
+ "name" : "segmentleft",
+ "size" : "8"
+ },
+ {
+ "name" : "lastentry",
+ "size" : "8"
+ },
+ {
+ "name" : "flags",
+ "size" : "8"
+ },
+ {
+ "name" : "tag",
+ "size" : "16"
+ },
+ {
+ "name" : "addresses",
+ "size" : "headerextlength<<6"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/l2tpv2ctl.json b/extras/packetforge/parsegraph/nodes/l2tpv2ctl.json
new file mode 100644
index 00000000000..7af22dd92fa
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/l2tpv2ctl.json
@@ -0,0 +1,74 @@
+{
+ "type" : "node",
+ "name" : "l2tpv2ctl",
+ "layout" : [
+ {
+ "name" : "t",
+ "size" : "1",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "l",
+ "size" : "1",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "reserved",
+ "size" : "2"
+ },
+ {
+ "name" : "s",
+ "size" : "1",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "reserved",
+ "size" : "1"
+ },
+ {
+ "name" : "o",
+ "size" : "1",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "p",
+ "size" : "1",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "reserved",
+ "size" : "4"
+ },
+ {
+ "name" : "version",
+ "size" : "4",
+ "default" : "2",
+ "readonly" : "true"
+ },
+ {
+ "name" : "length",
+ "size" : "16"
+ },
+ {
+ "name" : "tunnelid",
+ "size" : "16"
+ },
+ {
+ "name" : "sessionid",
+ "size" : "16"
+ },
+ {
+ "name" : "ns",
+ "size" : "16"
+ },
+ {
+ "name" : "nr",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/l2tpv2data.json b/extras/packetforge/parsegraph/nodes/l2tpv2data.json
new file mode 100644
index 00000000000..d9e8b006820
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/l2tpv2data.json
@@ -0,0 +1,79 @@
+{
+ "type" : "node",
+ "name" : "l2tpv2data",
+ "layout" : [
+ {
+ "name" : "t",
+ "size" : "1",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "l",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "2"
+ },
+ {
+ "name" : "s",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "1"
+ },
+ {
+ "name" : "o",
+ "size" : "1"
+ },
+ {
+ "name" : "p",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "4"
+ },
+ {
+ "name" : "version",
+ "size" : "4",
+ "default" : "2",
+ "readonly" : "true"
+ },
+ {
+ "name" : "length",
+ "size" : "16",
+ "optional" : "l=1"
+ },
+ {
+ "name" : "tunnelid",
+ "size" : "16"
+ },
+ {
+ "name" : "sessionid",
+ "size" : "16"
+ },
+ {
+ "name" : "ns",
+ "size" : "16",
+ "optional" : "s=1"
+ },
+ {
+ "name" : "nr",
+ "size" : "16",
+ "optional" : "s=1"
+ },
+ {
+ "name" : "offsetsize",
+ "size" : "16",
+ "optional" : "o=1"
+ },
+ {
+ "name" : "offsetpad",
+ "size" : "offsetsize<<3",
+ "optional" : "o=1"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/mac.json b/extras/packetforge/parsegraph/nodes/mac.json
new file mode 100644
index 00000000000..fab690b559d
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/mac.json
@@ -0,0 +1,22 @@
+{
+ "type" : "node",
+ "name" : "mac",
+ "layout" : [
+ {
+ "name" : "dst",
+ "size" : "48",
+ "format" : "mac",
+ "default" : "00:00:00:00:00:01"
+ },
+ {
+ "name" : "src",
+ "size" : "48",
+ "format" : "mac",
+ "default" : "00:00:00:00:00:02"
+ },
+ {
+ "name" : "ethertype",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/nvgre.json b/extras/packetforge/parsegraph/nodes/nvgre.json
new file mode 100644
index 00000000000..ec6812c8500
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/nvgre.json
@@ -0,0 +1,52 @@
+{
+ "type" : "node",
+ "name" : "nvgre",
+ "layout" : [
+ {
+ "name" : "c",
+ "size" : "1",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "reserved",
+ "size" : "1"
+ },
+ {
+ "name" : "k",
+ "size" : "1",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "s",
+ "size" : "1",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "reserved",
+ "size" : "9"
+ },
+ {
+ "name" : "version",
+ "size" : "3",
+ "default" : "0",
+ "readonly" : "true"
+ },
+ {
+ "name" : "protocoltype",
+ "size" : "16",
+ "default" : "0x6558",
+ "readonly" : "true"
+ },
+ {
+ "name" : "vsid",
+ "size" : "16"
+ },
+ {
+ "name" : "flowid",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/payload.json b/extras/packetforge/parsegraph/nodes/payload.json
new file mode 100644
index 00000000000..202116ff1ee
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/payload.json
@@ -0,0 +1,18 @@
+{
+ "type" : "node",
+ "name" : "payload",
+ "layout" : [
+ {
+ "name" : "data",
+ "size" : "bytes<<3",
+ "format" : "bytearray"
+ }
+ ],
+ "attributes" : [
+ {
+ "name" : "bytes",
+ "size" : "16",
+ "default" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/pfcp.json b/extras/packetforge/parsegraph/nodes/pfcp.json
new file mode 100644
index 00000000000..f1a6c1901bd
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/pfcp.json
@@ -0,0 +1,58 @@
+{
+ "type" : "node",
+ "name" : "pfcp",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "3",
+ "default" : "1",
+ "readonly" : "true"
+ },
+ {
+ "name" : "reserved",
+ "size" : "3"
+ },
+ {
+ "name" : "mp",
+ "size" : "1"
+ },
+ {
+ "name" : "s",
+ "size" : "1"
+ },
+ {
+ "name" : "messagetype",
+ "size" : "8"
+ },
+ {
+ "name" : "messagelength",
+ "size" : "16",
+ "default" : "8",
+ "autoincrease" : "true"
+ },
+ {
+ "name" : "seid",
+ "size" : "64",
+ "optional" : "s=1",
+ "increaselength" : "true"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "24"
+ },
+ {
+ "name" : "messagepriority",
+ "size" : "4",
+ "optional" : "mp=1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "4",
+ "optional" : "mp=0"
+ },
+ {
+ "name" : "reserved",
+ "size" : "4"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/sctp.json b/extras/packetforge/parsegraph/nodes/sctp.json
new file mode 100644
index 00000000000..58ef88cec55
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/sctp.json
@@ -0,0 +1,22 @@
+{
+ "type" : "node",
+ "name" : "sctp",
+ "layout" : [
+ {
+ "name" : "src",
+ "size" : "16"
+ },
+ {
+ "name" : "dst",
+ "size" : "16"
+ },
+ {
+ "name" : "veificationtag",
+ "size" : "16"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/tcp.json b/extras/packetforge/parsegraph/nodes/tcp.json
new file mode 100644
index 00000000000..7d0bac20ab1
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/tcp.json
@@ -0,0 +1,79 @@
+{
+ "type" : "node",
+ "name" : "tcp",
+ "layout" : [
+ {
+ "name" : "src",
+ "size" : "16"
+ },
+ {
+ "name" : "dst",
+ "size" : "16"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "32"
+ },
+ {
+ "name" : "acknowledgementnumber",
+ "size" : "32"
+ },
+ {
+ "name" : "dataoffset",
+ "size" : "4",
+ "default" : "5"
+ },
+ {
+ "name" : "reserved",
+ "size" : "3"
+ },
+ {
+ "name" : "ns",
+ "size" : "1"
+ },
+ {
+ "name" : "cwr",
+ "size" : "1"
+ },
+ {
+ "name" : "ece",
+ "size" : "1"
+ },
+ {
+ "name" : "urg",
+ "size" : "1"
+ },
+ {
+ "name" : "ack",
+ "size" : "1"
+ },
+ {
+ "name" : "psh",
+ "size" : "1"
+ },
+ {
+ "name" : "pst",
+ "size" : "1"
+ },
+ {
+ "name" : "syn",
+ "size" : "1"
+ },
+ {
+ "name" : "fin",
+ "size" : "1"
+ },
+ {
+ "name" : "windowsize",
+ "size" : "16"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16"
+ },
+ {
+ "name" : "urgentpointer",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/udp.json b/extras/packetforge/parsegraph/nodes/udp.json
new file mode 100644
index 00000000000..75a95aec4a9
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/udp.json
@@ -0,0 +1,24 @@
+{
+ "type" : "node",
+ "name" : "udp",
+ "layout" : [
+ {
+ "name" : "src",
+ "size" : "16"
+ },
+ {
+ "name" : "dst",
+ "size" : "16"
+ },
+ {
+ "name" : "length",
+ "size" : "16",
+ "default" : "8",
+ "autoincrease" : "true"
+ },
+ {
+ "name" : "checksum",
+ "size" : "16"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/vlan.json b/extras/packetforge/parsegraph/nodes/vlan.json
new file mode 100644
index 00000000000..4a87a14d12d
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/vlan.json
@@ -0,0 +1,30 @@
+{
+ "type" : "node",
+ "name" : "vlan",
+ "layout" : [
+ {
+ "name" : "pcp",
+ "size" : "3"
+ },
+ {
+ "name" : "dei",
+ "size" : "1"
+ },
+ {
+ "name" : "vid",
+ "size" : "12"
+ },
+ {
+ "name" : "ethertype",
+ "size" : "16",
+ "readonly" : "true"
+ }
+ ],
+ "attributes" : [
+ {
+ "name" : "tpid",
+ "size" : "16",
+ "default" : "0x8100"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/vxlan.json b/extras/packetforge/parsegraph/nodes/vxlan.json
new file mode 100644
index 00000000000..f797075aa84
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/vxlan.json
@@ -0,0 +1,33 @@
+{
+ "type" : "node",
+ "name" : "vxlan",
+ "layout" : [
+ {
+ "name" : "reserved",
+ "size" : "4"
+ },
+ {
+ "name" : "i",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "11"
+ },
+ {
+ "name" : "vni",
+ "size" : "24"
+ },
+ {
+ "name" : "reserved",
+ "size" : "8"
+ }
+ ],
+ "attributes" : [
+ {
+ "name" : "udpport",
+ "size" : "16",
+ "default" : "4789"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/nodes/vxlangpe.json b/extras/packetforge/parsegraph/nodes/vxlangpe.json
new file mode 100644
index 00000000000..711e8bbcd04
--- /dev/null
+++ b/extras/packetforge/parsegraph/nodes/vxlangpe.json
@@ -0,0 +1,53 @@
+{
+ "type" : "node",
+ "name" : "vxlangpe",
+ "layout" : [
+ {
+ "name" : "reserved",
+ "size" : "2"
+ },
+ {
+ "name" : "version",
+ "size" : "2"
+ },
+ {
+ "name" : "i",
+ "size" : "1"
+ },
+ {
+ "name" : "p",
+ "size" : "1"
+ },
+ {
+ "name" : "b",
+ "size" : "1"
+ },
+ {
+ "name" : "o",
+ "size" : "1"
+ },
+ {
+ "name" : "reserved",
+ "size" : "16"
+ },
+ {
+ "name" : "nextprotocol",
+ "size" : "8"
+ },
+ {
+ "name" : "vni",
+ "size" : "24"
+ },
+ {
+ "name" : "reserved",
+ "size" : "8"
+ }
+ ],
+ "attributes" : [
+ {
+ "name" : "udpport",
+ "size" : "16",
+ "default" : "4790"
+ }
+ ]
+}
diff --git a/extras/packetforge/parsegraph/samples/mac_ipv4.json b/extras/packetforge/parsegraph/samples/mac_ipv4.json
new file mode 100644
index 00000000000..c0a8d18177d
--- /dev/null
+++ b/extras/packetforge/parsegraph/samples/mac_ipv4.json
@@ -0,0 +1,24 @@
+{
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "ipv4",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "1.1.1.1",
+ "mask" : "255.255.255.255"
+ },
+ {
+ "name" : "dst",
+ "value" : "2.2.2.2",
+ "mask" : "255.255.255.255"
+ }
+ ]
+ }
+ ],
+ "actions" : "redirect-to-queue 3"
+}
diff --git a/extras/packetforge/parsegraph/samples/mac_ipv4_udp.json b/extras/packetforge/parsegraph/samples/mac_ipv4_udp.json
new file mode 100644
index 00000000000..7df27cfdf12
--- /dev/null
+++ b/extras/packetforge/parsegraph/samples/mac_ipv4_udp.json
@@ -0,0 +1,27 @@
+{
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "ipv4",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "1.1.1.1",
+ "mask" : "255.255.255.255"
+ },
+ {
+ "name" : "dst",
+ "value" : "2.2.2.2",
+ "mask" : "255.255.255.255"
+ }
+ ]
+ },
+ {
+ "header" : "udp"
+ }
+ ],
+ "actions" : "rss"
+} \ No newline at end of file
diff --git a/extras/packetforge/parsegraph/samples/mac_ipv4_udp_gtpu_gtppsc_ipv4.json b/extras/packetforge/parsegraph/samples/mac_ipv4_udp_gtpu_gtppsc_ipv4.json
new file mode 100644
index 00000000000..39a8d6159b4
--- /dev/null
+++ b/extras/packetforge/parsegraph/samples/mac_ipv4_udp_gtpu_gtppsc_ipv4.json
@@ -0,0 +1,43 @@
+{
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "ipv4"
+ },
+ {
+ "header" : "udp"
+ },
+ {
+ "header" : "gtpu",
+ "fields" : [
+ {
+ "name" : "teid",
+ "value" : "32",
+ "mask" : "0xffffffff"
+ }
+ ]
+ },
+ {
+ "header" : "gtppsc"
+ },
+ {
+ "header" : "ipv4",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "1.1.1.1",
+ "mask" : "255.255.255.255"
+ },
+ {
+ "name" : "dst",
+ "value" : "2.2.2.2",
+ "mask" : "255.255.255.255"
+ }
+ ]
+ }
+ ],
+ "actions" : "rss"
+}
diff --git a/extras/packetforge/parsegraph/samples/mac_ipv4_udp_vxlan_mac_ipv4.json b/extras/packetforge/parsegraph/samples/mac_ipv4_udp_vxlan_mac_ipv4.json
new file mode 100644
index 00000000000..346dcd27fcb
--- /dev/null
+++ b/extras/packetforge/parsegraph/samples/mac_ipv4_udp_vxlan_mac_ipv4.json
@@ -0,0 +1,43 @@
+{
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "ipv4"
+ },
+ {
+ "header" : "udp"
+ },
+ {
+ "header" : "vxlan",
+ "fields" : [
+ {
+ "name" : "vni",
+ "value" : "100",
+ "mask" : "0xffffff"
+ }
+ ]
+ },
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "ipv4",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "1.1.1.1",
+ "mask" : "255.255.255.255"
+ },
+ {
+ "name" : "dst",
+ "value" : "2.2.2.2",
+ "mask" : "255.255.255.255"
+ }
+ ]
+ }
+ ],
+ "actions" : "rss"
+}
diff --git a/extras/packetforge/parsegraph/samples/mac_ipv6.json b/extras/packetforge/parsegraph/samples/mac_ipv6.json
new file mode 100644
index 00000000000..17b6490c099
--- /dev/null
+++ b/extras/packetforge/parsegraph/samples/mac_ipv6.json
@@ -0,0 +1,44 @@
+{
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "00:00:00:00:00:01",
+ "mask" : "ff:ff:ff:ff:ff:ff"
+ },
+ {
+ "name" : "dst",
+ "value" : "00:00:00:00:00:02",
+ "mask" : "ff:ff:ff:ff:ff:ff"
+ },
+ {
+ "name" : "ethertype",
+ "value" : "0x2345"
+ }
+ ]
+ },
+ {
+ "header" : "ipv6",
+ "fields" : [
+ {
+ "name" : "flowlabel",
+ "value" : "1"
+ },
+ {
+ "name" : "src",
+ "value" : "0001:0002:0003:0004:0005:0006:0007:0008",
+ "mask" : "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+ },
+ {
+ "name" : "dst",
+ "value" : "0001:0002:0003:0004:0005:0006:0007:0008",
+ "mask" : "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+ }
+ ]
+ }
+ ],
+ "actions" : "rss"
+}
diff --git a/extras/packetforge/parsegraph/samples/mac_vlan_ipv4.json b/extras/packetforge/parsegraph/samples/mac_vlan_ipv4.json
new file mode 100644
index 00000000000..7905133a8e0
--- /dev/null
+++ b/extras/packetforge/parsegraph/samples/mac_vlan_ipv4.json
@@ -0,0 +1,34 @@
+{
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "vlan",
+ "fields" : [
+ {
+ "name" : "vid",
+ "value" : "100",
+ "mask" : "0xfff"
+ }
+ ]
+ },
+ {
+ "header" : "ipv4",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "1.1.1.1",
+ "mask" : "255.255.255.255"
+ },
+ {
+ "name" : "dst",
+ "value" : "2.2.2.2",
+ "mask" : "255.255.255.255"
+ }
+ ]
+ }
+ ],
+ "actions" : "rss"
+}
diff --git a/extras/packetforge/parsegraph/spec.md b/extras/packetforge/parsegraph/spec.md
new file mode 100644
index 00000000000..93ff7791f20
--- /dev/null
+++ b/extras/packetforge/parsegraph/spec.md
@@ -0,0 +1,533 @@
+#### Packet Forger JSON Specification Rev 0.1
+
+### 0. Change Logs
+
+2021-10, initialized by Zhang, Qi
+
+### 1. Parse Graph
+
+A Parse Graph is a unidirectional graph. It is consist of a set of nodes and edges. A node represent a network protocol header, and an edge represent the linkage of two protocol headers which is adjacent in the packet. An example of a parse graph have 5 nodes and 6 edges.
+
+[![](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBKChNQUMpKSAtLT4gQigoSVB2NCkpXG4gICAgQSgoTUFDKSkgLS0-IEMoKElQdjYpKVxuICAgIEIgLS0-IEQoKFRDUCkpXG4gICAgQyAtLT4gRCgoVENQKSlcbiAgICBCIC0tPiBFKChVRFApKVxuICAgIEMgLS0-IEUoKFVEUCkpXG4gICAgIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRhcmsifSwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ)](https://mermaid-js.github.io/mermaid-live-editor/edit#eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBKChNQUMpKSAtLT4gQigoSVB2NCkpXG4gICAgQSgoTUFDKSkgLS0-IEMoKElQdjYpKVxuICAgIEIgLS0-IEQoKFRDUCkpXG4gICAgQyAtLT4gRCgoVENQKSlcbiAgICBCIC0tPiBFKChVRFApKVxuICAgIEMgLS0-IEUoKFVEUCkpXG4gICAgIiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRhcmtcIlxufSIsInVwZGF0ZUVkaXRvciI6ZmFsc2UsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjpmYWxzZX0)
+
+A Node or an Edge is described by a json object. There is no json representation for a parse graph, software should load all json objects of nodes and edges then build the parse graph logic in memory.
+
+### 2. Node
+
+A json object of Node will include below properties:
+
+* **type**
+
+ This should always be "node".
+
+* **name**
+
+ This is the name of the protocol.
+
+* **layout**
+
+ This is an array of fields in the protocol header which also imply the bit order. For example, json object of mac header as below:
+ ```
+ {
+ "type" : "node",
+ "name" : "mac",
+ "layout" : [
+ {
+ "name" : "src",
+ "size" : "48",
+ "format" : "mac",
+ },
+ {
+ "name" : "dst",
+ "size" : "48",
+ "format" : "mac",
+ },
+ {
+ "name" : "ethertype",
+ "size" : "16",
+ }
+ ]
+ }
+ ```
+
+ For each field, there are properties can be defined:
+
+ * **name**
+
+ The name of the field, typically it should be unique to all fields in the same node, except when it is "reserved".
+
+ * **size**
+
+ Size of the field, note, the unit is "bit" but not "byte".
+ Sometime a field's size can be decided by another field's value, for example, a geneve header's "options" field's size is decided by "optlen" field's value, so we have below:
+
+ ```
+ "name" : "geneve",
+ "layout" : [
+
+ ......
+
+ {
+ "name" : "reserved",
+ "size" : "8"
+ },
+ {
+ "name" : "options",
+ "size" : "optlen<<5"
+ }
+ ],
+ ```
+ Since when "optlen" increases 1 which means 4 bytes (32 bits) increase of "options"'s size so the bit value should shift left 5.
+
+ * **format**
+
+ Defined the input string format of the value, all formats are described in the section **Input Format** which also described the default format if it is not explicitly defined.
+
+ * **default**
+
+ Defined the default value of the field when a protocol header instance is created by the node. If not defined, the default value is always 0. The default value can be overwritten when forging a packet with specific value of the field. For example, we defined the default ipv4 address as below:
+
+ ```
+ "name" : "ipv4",
+ "layout" : [
+
+ ......
+
+ {
+ "name" : "src",
+ "size" : "32",
+ "format" : "ipv4",
+ "default" : "1.1.1.1"
+ },
+ {
+ "name" : "dst",
+ "size" : "32",
+ "format" : "ipv4",
+ "default" : "2.2.2.2"
+ }
+ ]
+ ```
+
+ * **readonly**
+
+ Define if a field is read only or not, typically it will be used together with "default". For example, the version of IPv4 header should be 4 and can't be overwritten.
+
+ ```
+ "name" : "ipv4",
+ "layout" : [
+ {
+ "name" : "version",
+ "size" : "4",
+ "default" : "4",
+ "readonly" : "true"
+ },
+ ......
+ ],
+ ```
+ A reserved field implies it is "readonly" and should always be 0.
+
+ * **optional**
+
+ A field could be optional depends on some flag as another field. For example, the GRE header has couple optional fields.
+
+ ```
+ "name" : "gre",
+ "layout" : [
+ {
+ "name" : "c",
+ "size" : "1",
+ },
+ {
+ "name" : "reserved",
+ "size" : "1",
+ },
+ {
+ "name" : "k",
+ "size" : "1",
+ },
+ {
+ "name" : "s",
+ "size" : "1",
+ },
+
+ ......
+
+ {
+ "name" : "checksum",
+ "size" : "16",
+ "optional" : "c=1",
+ },
+ {
+ "name" : "reserved",
+ "size" : "16",
+ "optional" : "c=1",
+ },
+ {
+ "name" : "key",
+ "size" : "32",
+ "optional" : "k=1"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "32",
+ "optional" : "s=1"
+ }
+ ]
+ ```
+
+ The expresion of an optional field can use "**&**" or "**|**" combine multiple conditions, for example for gtpu header, we have below optional fields.
+
+ ```
+ "name" : "gtpu",
+ "layout" : [
+
+ ......
+
+ {
+ "name" : "e",
+ "size" : "1"
+ },
+ {
+ "name" : "s",
+ "size" : "1"
+ },
+ {
+ "name" : "pn",
+ "size" : "1"
+ },
+
+ ......
+
+ {
+ "name" : "teid",
+ "size" : "16"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "16",
+ "optional" : "e=1|s=1|pn=1",
+ },
+
+ ......
+ ]
+
+ ```
+
+ * **autoincrease**
+
+ Some field's value cover the length of the payload or size of an optional field in the same header, so it should be auto increased during packet forging. For example the "totallength" of ipv4 header is a autoincrease feild.
+
+ ```
+ "name" : "ipv4",
+ "layout" : [
+
+ ......
+
+ {
+ "name" : "totallength",
+ "size" : "16",
+ "default" : "20",
+ "autoincrease" : "true",
+ },
+
+ ......
+
+ ]
+ ```
+
+ A field which is autoincrease also imply its readonly.
+
+ * **increaselength**
+
+ Typically this should only be enabled for an optional field to trigger another field's autoincrease. For example, the gtpc's "messagelength" field cover all the data start from field "teid", so its default size is 4 bytes which cover sequencenumber + 8 reserved bit, and should be increased if "teid" exist or any payload be appended.
+
+ ```
+ "name" : "gtpc",
+ "layout" : [
+
+ ......
+
+ {
+ "name" : "messagelength",
+ "size" : "16",
+ "default" : "4",
+ "autoincrease" : "true",
+ },
+ {
+ "name" : "teid",
+ "size" : "32",
+ "optional" : "t=1",
+ "increaselength" : "true"
+ },
+ {
+ "name" : "sequencenumber",
+ "size" : "24",
+ },
+ {
+ "name" : "reserved",
+ "size" : "8",
+ }
+ ]
+ ```
+
+* **attributes**
+
+ This defines an array of attributes, the attribute does not define the data belongs to current protocol header, but it impact the behaviour during applying actions of an edge when the protocol header is involved. For example, a geneve node has attribute "udpport" which define the udp tunnel port, so when it is appended after a udp header, the udp header's dst port is expected to be changed to this value.
+
+ ```
+ "name" : "geneve",
+
+ "fields" : [
+
+ ......
+
+ ],
+ "attributes" : [
+ {
+ "name" : "udpport",
+ "size" : "16",
+ "default" : "6081"
+ }
+ ]
+ ```
+
+ An attribute can only have below properties which take same effect when they are in field.
+
+ * name
+ * size (must be fixed value)
+ * default
+ * format
+
+### 3. Edge
+
+ A json object of Edge will include below properties:
+
+ * **type**
+
+ This should always be "edge".
+
+ * **start**
+
+ This is the start node of the edge.
+
+ * **end**
+
+ This is the end node of the edge.
+
+ * **actions**
+
+ This is an array of actions the should be applied during packet forging.
+ For example, when append a ipv4 headers after a mac header, the "ethertype" field of mac should be set to "0x0800":
+
+ ```
+ {
+ "type" : "edge",
+ "start" : "mac",
+ "end" : "ipv4",
+ "actions" : [
+ {
+ "dst" : "start.ethertype",
+ "src" : "0x0800"
+ }
+ ]
+ }
+ ```
+ Each action should have two properties:
+
+ * **dst**
+
+ This describe the target field to set, it is formatted as <node>.<field>
+ node must be "start" or "end".
+
+ * **src**
+
+ This describe the value to set, it could be a const value or same format as dst's.
+ For example when append a vlan header after mac, we will have below actions:
+
+ ```
+ {
+ "type" : "edge",
+ "start" : "mac",
+ "end" : "vlan",
+ "actions" : [
+ {
+ "dst" : "start.ethertype",
+ "src" : "end.tpid"
+ },
+ {
+ "dst" : "end.ethertype",
+ "src" : "start.ethertype"
+ }
+ ]
+ }
+ ```
+
+
+ To avoid duplication, multiple edges can be aggregate into the one json object if there actions are same. So, multiple node name can be added to **start** or **end** with seperateor "**,**".
+
+ For example, all ipv6 and ipv6 extention header share the same actions when append a udp header
+
+ ```
+ {
+ "type" : "edge",
+ "start" : "ipv6,ipv6srh,ipv6crh16,ipv6crh32",
+ "end" : "udp",
+ "actions" : [
+ {
+ "dst" : "start.nextheader",
+ "src" : "17"
+ }
+ ]
+ }
+ ```
+
+ Another examples is gre and nvgre share the same actions when be appanded after a ipv4 header:
+ ```
+ {
+ "type" : "edge",
+ "start" : "ipv4",
+ "end" : "gre,nvgre",
+ "actions" : [
+ {
+ "dst" : "start.protocol",
+ "src" : "47"
+ }
+ ]
+ }
+ ```
+
+### 4. Path
+
+A path defines a sequence of nodes which is the input parameter for a packet forging, a packet forging should fail if the path can't be recognised as a subgraph of the parser graph.
+
+A json object of a path should include below properties:
+
+* **type**
+
+ This should always be "path".
+
+* **stack**
+
+ This is an array of node configurations which also imply the protocol header sequence of a packet. Below is an example to forge an ipv4 / udp packet with default value.
+
+ ```
+ {
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac"
+ },
+ {
+ "header" : "ipv4"
+ },
+ {
+ "header" : "udp"
+ },
+ ]
+ }
+ ```
+
+ A node configuration can have below properties:
+
+ * **header**
+
+ This is a protocol name (a node name).
+
+ * **fields**
+
+ This is an array of 3 member tuples:
+
+ * **name**
+
+ The name of the field or attribute that belongs to the node, note a readonly field should not be selected.
+
+ * **value**
+
+ The value to set the field or attribute.
+
+ * **mask**
+
+ This is optional, if it is not defined, corresponding bit of the mask should be set to 0, and it should be ignored for an attribute.
+
+* **actions**
+
+ This is optional. When this json file is the input of flow adding commands, it can be used directly as the flow rule's action.
+
+ An example to forge a ipv4 packet with src ip address 192.168.0.1 and dst ip address 192.168.0.2, also take ip address as mask.
+
+ ```
+ {
+ "type" : "path",
+ "stack" : [
+ {
+ "header" : "mac",
+ },
+ {
+ "header" : "ipv4",
+ "fields" : [
+ {
+ "name" : "src",
+ "value" : "192.168.0.1",
+ "mask" : "255.255.255.255"
+ },
+ {
+ "name" : "dst",
+ "value" : "192.168.0.2",
+ "mask" : "255.255.255.255"
+ }
+ ]
+ }
+ ],
+ "actions" : "redirect-to-queue 3"
+ }
+ ```
+
+
+### 5. Input Format
+
+Every field or attribute is associated with an **Input Format**, so the software can figure out how to parse default value in the node or a config value in the path.
+
+Currently we have 8 predefined format and don't support customised format.
+
+* **u8**
+
+ accept number from 0 to 255 or hex from 0x0 to 0xff.
+
+* **u16**
+
+ accept number from 0 to 65535 or hex from 0x0 to 0xffff.
+
+* **u32**
+
+ accept number from 0 to 4294967295 or hex from 0x0 to 0xffffffff
+
+* **u64**
+
+ accept number from 0 to 2^64 -1 or hex from 0x0 to 0xffffffffffffffff
+
+* **mac**
+
+ accept xx:xx:xx:xx:xx:xx , x in hex from 0 to f
+
+* **ipv4**
+
+ accept n.n.n.n , n from 0 to 255
+
+* **ipv6**
+
+ accept xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx, x in hex from 0 to f
+
+* **bytearray**
+
+ accept u8,u8,u8.....
+
+If format is not defined for a field or attribute, the default format will be selected base on size as below, and the MSB should be ignored by software if the value exceeds the limitation.
+
+| Size | Default Format |
+| ------------- | -------------- |
+| 1 - 8 | u8 |
+| 9 - 16 | u16 |
+| 17 - 32 | u32 |
+| 33 - 64 | u64 |
+| > 64 | bytearray |
+| variable size | bytearray |
diff --git a/extras/rpm/Makefile b/extras/rpm/Makefile
index b06c9fb9675..0c422c7994c 100644
--- a/extras/rpm/Makefile
+++ b/extras/rpm/Makefile
@@ -26,6 +26,13 @@ PC=%
all: RPM
+# SUSE osleap15
+ifeq ($(filter opensuse-leap,$(OS_ID)),$(OS_ID))
+SRC_SPEC_DIR="opensuse"
+else
+SRC_SPEC_DIR="."
+endif
+
SPEC_FILE='vpp.spec'
spec:
@@ -33,7 +40,7 @@ spec:
mkdir -p $(RPMBUILD)/RPMS $(RPMBUILD)/SRPMS $(RPMBUILD)/BUILD \
$(RPMBUILD)/SOURCES $(RPMBUILD)/SPECS
cp $(TARBALL) $(RPMBUILD)/SOURCES/vpp-$(VERSION)-$(RELEASE).tar.xz
- cp $(SPEC_FILE) $(RPMBUILD)/SPECS
+ cp $(SRC_SPEC_DIR)/$(SPEC_FILE) $(RPMBUILD)/SPECS
srpm: spec
rpmbuild -bs \
diff --git a/extras/rpm/opensuse/Dockerfile b/extras/rpm/opensuse/Dockerfile
new file mode 100644
index 00000000000..34a39ea7765
--- /dev/null
+++ b/extras/rpm/opensuse/Dockerfile
@@ -0,0 +1,16 @@
+# Run from top of vpp repo with command:
+# docker build -f extras/rpm/opensuse/Dockerfile .
+
+ARG SUSE_VERSION=15.4
+
+FROM opensuse/leap:${SUSE_VERSION} as vppbuild
+COPY . /vpp
+WORKDIR /vpp
+RUN zypper refresh
+RUN zypper install -y make sudo
+COPY . .
+RUN UNATTENDED=y make install-dep
+RUN ln -s /usr/bin/cmake /usr/bin/cmake3
+RUN UNATTENDED=y make install-ext-deps
+RUN make pkg-rpm
+CMD ["/bin/bash"]
diff --git a/extras/rpm/opensuse/README.md b/extras/rpm/opensuse/README.md
new file mode 100644
index 00000000000..6f3cadfc70e
--- /dev/null
+++ b/extras/rpm/opensuse/README.md
@@ -0,0 +1,89 @@
+# Build RPM for openSUSE
+
+## Introduction
+
+This is to describe how to compile and create installable RPM packages for openSUSE leap.
+In general you should visit [Pulling, Building,
+Running, Hacking, Pushing](https://wiki.fd.io/view/VPP/Pulling,_Building,_Run
+ning,_Hacking_and_Pushing_VPP_Code) which provides full description for other type of system (Ubuntu,Centos or Redhat).
+
+## Get the VPP Sources
+
+To get the VPP sources that are used to create the build, run the following commands:
+
+```bash
+# git clone https://gerrit.fd.io/r/vpp
+# cd vpp
+```
+
+There are two ways to continue:
+
+* Build by docker
+* Build on your own openSUSE system
+
+## Build by Docker
+
+Run the following docker command:
+
+```bash
+docker build -f extras/rpm/opensuse/Dockerfile .
+```
+
+The packages now can be copied from the docker image and can be installed on openSUSE.
+An example how to extend the Dockerfile to install vpp:
+
+'''dockerfile
+FROM opensuse/leap:${SUSE_VERSION} as vppinstall
+COPY --from=vppbuild /vpp/build-root/*rpm /rpms/
+RUN VPP_INSTALL_SKIP_SYSCTL=false zypper install --allow-unsigned-rpm -y --no-recommends /rpms/*.rpm;\
+...
+'''
+
+## Build on openSUSE
+
+### Build VPP Dependencies
+
+Before building a VPP image, make sure there are no FD.io VPP or DPDK packages installed, by entering the following commands:
+
+```bash
+# rpm -ql vpp
+package vpp is not installed
+# rpm -ql dpdk
+package dpdk is not installed
+
+```
+
+Run the following make command to install the dependencies for FD.io VPP.
+
+```bash
+make install-dep
+```
+
+Run the following make command to install the external dependencies for FD.io VPP.
+
+```bash
+ln -s /usr/bin/cmake /usr/bin/cmake3 # some thirdparty checking for cmake3 binary
+make install-ext-dep
+```
+
+### Build RPM Packages
+
+Create packages for openSUSE by running:
+
+```bash
+make pkg-rpm
+```
+
+Once the packages are built they can be found in the build-root directory.
+
+```bash
+# ls *.rpm
+```
+
+If the packages are built correctly, then this should be the corresponding output:
+
+```bash
+build-root/libvpp0-21.10-rc0~200_gb89ae9670.x86_64.rpm build-root/vpp-api-python-21.10-rc0~200_gb89ae9670.x86_64.rpm
+build-root/vpp-21.10-rc0~200_gb89ae9670.x86_64.rpm build-root/vpp-devel-21.10-rc0~200_gb89ae9670.x86_64.rpm
+build-root/vpp-api-lua-21.10-rc0~200_gb89ae9670.x86_64.rpm build-root/vpp-plugins-21.10-rc0~200_gb89ae9670.x86_64.rpm
+```
diff --git a/extras/rpm/opensuse/vpp.spec b/extras/rpm/opensuse/vpp.spec
new file mode 100644
index 00000000000..11a10d63340
--- /dev/null
+++ b/extras/rpm/opensuse/vpp.spec
@@ -0,0 +1,320 @@
+#
+# spec file for package vpp
+#
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+#
+# Copyright (c) 2022 Nordix Foundation.
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+%define _vpp_build_dir %{buildroot}/../../BUILD/vpp-%{version}/build-root
+%define _vpp_install_dir %{_vpp_build_dir}/install-vpp-native/
+%define _vpp_plugins_lib_dir %{_vpp_install_dir}/vpp/lib64
+
+%define lname libvpp0
+
+Name: vpp
+Version: %{_version}
+Release: %{_release}
+Summary: Set of libraries and drivers for fast packet processing
+License: Apache-2.0
+Group: Productivity/Networking/Routing
+Url: https://wiki.fd.io/view/VPP
+Source0: %{name}-%{version}-%{_release}.tar.xz
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: bison
+BuildRequires: ccache
+BuildRequires: clang
+BuildRequires: check-devel
+BuildRequires: chrpath
+BuildRequires: distribution-release
+BuildRequires: gcc
+BuildRequires: gcc-c++
+BuildRequires: glibc-devel
+BuildRequires: glibc-devel-static
+BuildRequires: libnuma-devel
+BuildRequires: libopenssl-devel
+BuildRequires: libtool
+BuildRequires: lsb-release
+BuildRequires: make
+BuildRequires: openssl-devel
+BuildRequires: python-rpm-macros
+BuildRequires: python3-devel
+BuildRequires: python3-pip
+BuildRequires: python3-ply
+BuildRequires: shadow
+Conflicts: otherproviders(vpp-any)
+Provides: %{name}-any = %{version}
+ExclusiveArch: x86_64 aarch64
+%if 0%{?suse_version} >= 1210
+BuildRequires: systemd-rpm-macros
+%endif
+
+%description
+The Vector Packet Processing platform is a framework that provides
+switch/router functionality. It is based on Cisco's packet processing
+stack that can run on commodity CPUs.
+This package provides VPP executables: vpp, vpp_api_test, vpp_json_test
+vpp - the vector packet engine
+vpp_api_test - vector packet engine API test tool
+vpp_json_test - vector packet engine JSON test tool
+
+%package -n %{lname}
+Summary: VPP libraries
+Group: System/Libraries
+Provides: %{lname}-any = %{version}
+
+%description -n %{lname}
+This package contains the VPP shared libraries, including:
+vppinfra - foundation library supporting vectors, hashes, bitmaps, pools, and string formatting.
+svm - vm library
+vlib - vector processing library
+vlib-api - binary API library
+vnet - network stack library
+
+%package devel
+Summary: VPP header files, static libraries
+Group: Development/Libraries/C and C++
+Requires: %{lname} = %{version}
+Conflicts: otherproviders(%{name}-any-devel)
+Provides: %{name}-any-devel = %{version}
+
+%description devel
+This package contains the header files for VPP.
+Install this package if you want to write a
+program for compilation and linking with vpp lib.
+vlib
+vlibmemory
+vnet - devices, classify, dhcp, ethernet flow, gre, ip, etc.
+vpp-api
+vppinfra
+
+%package plugins
+Summary: Vector Packet Processing--runtime plugins
+Group: Productivity/Networking/Routing
+Conflicts: otherproviders(%{name}-any-plugins)
+Provides: %{name}-any-plugins = %{version}
+
+%description plugins
+This package contains the VPP plugins which are loaded by VPP at startup
+
+%package api-lua
+Summary: VPP api lua bindings
+Group: Development/Libraries/Other
+Requires: %{lname} = %{version}
+Requires: %{name} = %{version}
+Requires: %{name}-devel = %{version}
+Conflicts: otherproviders(%{name}-any-api-lua)
+Provides: %{name}-any-api-lua = %{version}
+
+%description api-lua
+This package contains the lua bindings for the vpp api
+
+%package api-python
+Summary: VPP api python bindings
+Group: Development/Libraries/Python
+Requires: %{lname} = %{version}
+Requires: %{name} = %{version}
+Requires: %{name}-devel = %{version}
+Requires: python3-setuptools
+Conflicts: otherproviders(%{name}-any-python-api)
+Provides: %{name}-any-python-api = %{version}
+
+%description api-python
+This package contains the python bindings for the vpp api
+
+%prep
+%setup -q -n %{name}-%{version}
+
+%debug_package
+
+%build
+export VPP_BUILD_USER=suse
+export VPP_BUILD_HOST=SUSE
+
+make -C build-root V=1 PLATFORM=vpp TAG=vpp install-packages
+
+cd %{_vpp_build_dir}/../src/vpp-api/python && %{py3_build}
+
+%pre
+# Add the vpp group
+getent group vpp >/dev/null || groupadd -r vpp
+%service_add_pre vpp.service
+
+%install
+#
+# binaries
+#
+mkdir -p -m755 %{buildroot}%{_bindir}
+mkdir -p -m755 %{buildroot}%{_unitdir}
+install -m 755 %{_vpp_install_dir}/*/bin/* %{buildroot}%{_bindir}
+
+# api
+mkdir -p -m755 %{buildroot}%{_datadir}/vpp/api
+
+#
+# core api
+#
+mkdir -p -m755 %{buildroot}%{_datadir}/vpp/api
+install -p -m 644 %{_vpp_install_dir}/vpp/share/vpp/api/core/*.api.json %{buildroot}%{_datadir}/vpp/api
+
+#
+# configs
+#
+mkdir -p -m755 %{buildroot}%{_sysconfdir}/vpp
+mkdir -p -m755 %{buildroot}%{_sysconfdir}/sysctl.d
+install -p -m 644 %{_vpp_build_dir}/../extras/rpm/vpp.service %{buildroot}%{_unitdir}
+install -p -m 644 %{_vpp_build_dir}/../src/vpp/conf/startup.conf %{buildroot}%{_sysconfdir}/vpp/startup.conf
+sed -i -e "s|^\(\s*\)api-trace {|plugin_path /usr/lib64/vpp_plugins\n\napi-trace {|" %{buildroot}%{_sysconfdir}/vpp/startup.conf
+install -p -m 644 %{_vpp_build_dir}/../src/vpp/conf/80-vpp.conf %{buildroot}%{_sysconfdir}/sysctl.d
+#
+# libraries
+#
+mkdir -p -m755 %{buildroot}%{_libdir}
+mkdir -p -m755 %{buildroot}%{_sysconfdir}/bash_completion.d
+mkdir -p -m755 %{buildroot}%{_datadir}/vpp
+for file in $(find %{_vpp_install_dir}/*/lib* -type f -name '*.so.*.*' -print )
+do
+ install -p -m 755 $file %{buildroot}%{_libdir}
+done
+for file in $(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e 's/^\.\///')
+do
+ # make lib symlinks
+ ( cd %{buildroot}%{_libdir} &&
+ ln -fs $file $(echo $file | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') )
+ ( cd %{buildroot}%{_libdir} &&
+ ln -fs $file $(echo $file | sed -e 's/\(\.so\)\.[0-9]\+.*/\1/') )
+done
+for file in $(find %{_vpp_install_dir}/vpp/share/vpp/api -type f -name '*.api.json' -print )
+do
+ install -p -m 644 $file %{buildroot}%{_datadir}/vpp/api
+done
+
+# Lua bindings
+mkdir -p -m755 %{buildroot}%{_datadir}/doc/vpp/examples/lua/examples/cli
+mkdir -p -m755 %{buildroot}%{_datadir}/doc/vpp/examples/lua/examples/lute
+for file in $(cd %{_vpp_install_dir}/../../src/vpp-api/lua && git ls-files .)
+do
+ install -p -m 644 %{_vpp_install_dir}/../../src/vpp-api/lua/$file \
+ %{buildroot}%{_datadir}/doc/vpp/examples/lua/$file
+done
+
+# Python bindings
+cd %{_vpp_build_dir}/../src/vpp-api/python && %{py3_install}
+
+#
+# devel
+#
+for dir in %{_vpp_install_dir}/vpp/include/
+do
+ for subdir in $(cd ${dir} && find . -type d -print)
+ do
+ mkdir -p -m755 %{buildroot}%{_includedir}/${subdir}
+ done
+ for file in $(cd ${dir} && find . -type f -print)
+ do
+ install -p -m 644 $dir/$file %{buildroot}%{_includedir}/$file
+ done
+done
+
+# sample plugin
+mkdir -p -m755 %{buildroot}%{_datadir}/doc/vpp/examples/sample-plugin/sample
+for file in $(cd %{_vpp_install_dir}/../../sample-plugin && find -type f -print)
+do
+ install -p -m 644 %{_vpp_install_dir}/../../sample-plugin/$file \
+ %{buildroot}%{_datadir}/doc/vpp/examples/sample-plugin/$file
+done
+
+#
+# vpp-plugins
+#
+mkdir -p -m755 %{buildroot}%{_libdir}/vpp_plugins
+mkdir -p -m755 %{buildroot}%{_libdir}/vpp_api_test_plugins
+for file in $(cd %{_vpp_plugins_lib_dir}/vpp_plugins && find -type f -print)
+do
+ install -p -m 644 %{_vpp_plugins_lib_dir}/vpp_plugins/$file \
+ %{buildroot}/%{_libdir}/vpp_plugins/$file
+done
+
+for file in $(cd %{_vpp_plugins_lib_dir}/vpp_api_test_plugins && find -type f -print)
+do
+ install -p -m 644 %{_vpp_plugins_lib_dir}/vpp_api_test_plugins/$file \
+ %{buildroot}/%{_libdir}/vpp_api_test_plugins/$file
+done
+
+for file in $(find %{_vpp_install_dir}/vpp/share/vpp/api/plugins -type f -name '*.api.json' -print )
+do
+ install -p -m 644 $file %{buildroot}%{_datadir}/vpp/api
+done
+
+#
+# remove RPATH from ELF binaries
+#
+%{_vpp_build_dir}/scripts/remove-rpath %{buildroot}
+
+export NO_BRP_CHECK_RPATH=true
+
+%post
+%service_add_post vpp.service
+
+%post -n %{lname} -p /sbin/ldconfig
+
+%preun
+%service_del_preun vpp.service
+
+%postun
+%service_del_postun vpp.service
+
+%postun -n %{lname} -p /sbin/ldconfig
+
+%files
+%{_unitdir}/vpp.service
+%{_bindir}/vpp*
+%{_bindir}/svm*
+%{_bindir}/vat2*
+%dir %{_sysconfdir}/vpp
+%config %{_sysconfdir}/sysctl.d/80-vpp.conf
+%config %{_sysconfdir}/vpp/startup.conf
+%license LICENSE
+
+%files -n %{lname}
+%exclude %{_libdir}/vpp_plugins
+%exclude %{_libdir}/vpp_api_test_plugins
+%{_libdir}/*.so.*
+
+%files api-lua
+%{_datadir}/doc/vpp/examples/lua
+
+%files api-python
+%dir %{python3_sitelib}/vpp_*
+%{python3_sitelib}/vpp_*
+
+%files devel
+%dir %{_datadir}/doc/vpp
+%dir %{_datadir}/doc/vpp/examples
+/usr/bin/vppapigen
+/usr/bin/vapi_c_gen.py
+/usr/bin/vapi_cpp_gen.py
+/usr/bin/vapi_json_parser.py
+%{_libdir}/*.so
+%{_includedir}/*
+%{_datadir}/doc/vpp/examples/sample-plugin
+%dir %{_datadir}/vpp
+%dir %{_datadir}/vpp/api
+%{_datadir}/vpp/api/*
+
+%files plugins
+%dir %{_libdir}/vpp_plugins
+%dir %{_libdir}/vpp_api_test_plugins
+%{_libdir}/vpp_plugins/*.so*
+%{_libdir}/vpp_api_test_plugins/*.so*
+
+%changelog
diff --git a/extras/rpm/vpp.spec b/extras/rpm/vpp.spec
index 9d798056ee5..6bfb12471e2 100644
--- a/extras/rpm/vpp.spec
+++ b/extras/rpm/vpp.spec
@@ -37,11 +37,10 @@ Version: %{_version}
Release: %{_release}
BuildRequires: systemd, chrpath
BuildRequires: check, check-devel
-BuildRequires: mbedtls-devel mbedtls
%if 0%{?fedora}
Requires: vpp-lib = %{_version}-%{_release}, vpp-selinux-policy = %{_version}-%{_release}, net-tools, pciutils
Requires: compat-openssl10
-Requires: boost-filesystem mbedtls libffi-devel
+Requires: libffi-devel
BuildRequires: subunit, subunit-devel
BuildRequires: compat-openssl10-devel
BuildRequires: python, python-devel, python-virtualenv, python-ply
@@ -49,28 +48,35 @@ BuildRequires: python3, python36-devel, python3-virtualenv
BuildRequires: cmake
%else
%if 0%{rhel} >= 7
-Requires: vpp-lib = %{_version}-%{_release}, vpp-selinux-policy = %{_version}-%{_release}, net-tools, pciutils, python36
-Requires: boost-filesystem mbedtls libffi-devel
+Requires: vpp-lib = %{_version}-%{_release}, vpp-selinux-policy = %{_version}-%{_release}, net-tools, pciutils
+Requires: libffi-devel
BuildRequires: epel-release
-BuildRequires: mbedtls-devel mbedtls
BuildREquires: openssl-devel
-BuildRequires: python36-devel
%if 0%{rhel} == 7
+Requires: python36
BuildRequires: devtoolset-9-toolchain
BuildRequires: cmake3
BuildRequires: glibc-static, yum-utils
+BuildRequires: python36-devel
%else
+%if 0%{rhel} == 8
+Requires: python36
BuildRequires: cmake
BuildRequires: dnf-utils
+BuildRequires: python36-devel
+%else
+Requires: python3
+BuildRequires: cmake
+BuildRequires: dnf-utils
+BuildRequires: python3-devel
+%endif
%endif
%endif
%endif
BuildRequires: libffi-devel
-BuildRequires: redhat-lsb
BuildRequires: apr-devel
BuildRequires: numactl-devel
BuildRequires: autoconf automake libtool byacc bison flex
-BuildRequires: boost boost-devel
BuildRequires: selinux-policy selinux-policy-devel
Source: %{name}-%{_version}-%{_release}.tar.xz
@@ -274,18 +280,25 @@ mkdir -p -m755 %{buildroot}%{_localstatedir}/log/vpp
#
# vpp-plugins
#
-mkdir -p -m755 %{buildroot}/usr/lib/vpp_plugins
-mkdir -p -m755 %{buildroot}/usr/lib/vpp_api_test_plugins
-for file in $(cd %{_mu_build_dir}/%{_vpp_install_dir}/vpp/lib/vpp_plugins && find -type f -print)
+mkdir -p -m755 %{buildroot}/usr/%{_lib}/vpp_plugins
+mkdir -p -m755 %{buildroot}/usr/%{_lib}/vpp_api_test_plugins
+mkdir -p -m755 %{buildroot}/usr/%{_lib}/vat2_plugins
+for file in $(cd %{_mu_build_dir}/%{_vpp_install_dir}/vpp/%{_lib}/vpp_plugins && find -type f -print)
+do
+ install -p -m 755 %{_mu_build_dir}/%{_vpp_install_dir}/vpp/%{_lib}/vpp_plugins/$file \
+ %{buildroot}/usr/%{_lib}/vpp_plugins/$file
+done
+
+for file in $(cd %{_mu_build_dir}/%{_vpp_install_dir}/vpp/%{_lib}/vpp_api_test_plugins && find -type f -print)
do
- install -p -m 755 %{_mu_build_dir}/%{_vpp_install_dir}/vpp/lib/vpp_plugins/$file \
- %{buildroot}/usr/lib/vpp_plugins/$file
+ install -p -m 755 %{_mu_build_dir}/%{_vpp_install_dir}/vpp/%{_lib}/vpp_api_test_plugins/$file \
+ %{buildroot}/usr/%{_lib}/vpp_api_test_plugins/$file
done
-for file in $(cd %{_mu_build_dir}/%{_vpp_install_dir}/vpp/lib/vpp_api_test_plugins && find -type f -print)
+for file in $(cd %{_mu_build_dir}/%{_vpp_install_dir}/vpp/%{_lib}/vat2_plugins && find -type f -print)
do
- install -p -m 755 %{_mu_build_dir}/%{_vpp_install_dir}/vpp/lib/vpp_api_test_plugins/$file \
- %{buildroot}/usr/lib/vpp_api_test_plugins/$file
+ install -p -m 755 %{_mu_build_dir}/%{_vpp_install_dir}/vpp/%{_lib}/vat2_plugins/$file \
+ %{buildroot}/usr/%{_lib}/vat2_plugins/$file
done
for file in $(find %{_mu_build_dir}/%{_vpp_install_dir}/vpp/share/vpp/api/plugins -type f -name '*.api.json' -print )
@@ -317,7 +330,7 @@ fi
%postun
-%systemd_postun
+%systemd_postun vpp.service
if [ $1 -eq 0 ] ; then
echo "Uninstalling, unbind user-mode PCI drivers"
# Unbind user-mode PCI drivers
@@ -371,6 +384,7 @@ fi
%global __requires_exclude_from %{_libdir}/librte_pmd_mlx[45]_glue\\.so.*$
%exclude %{_libdir}/vpp_plugins
%exclude %{_libdir}/vpp_api_test_plugins
+%exclude %{_libdir}/vat2_plugins
%{_libdir}/*
/usr/share/vpp/api/*
@@ -399,6 +413,7 @@ fi
%files plugins
%defattr(-,bin,bin)
-/usr/lib/vpp_plugins/*
-/usr/lib/vpp_api_test_plugins/*
+/usr/%{_lib}/vpp_plugins/*
+/usr/%{_lib}/vpp_api_test_plugins/*
+/usr/%{_lib}/vat2_plugins/*
/usr/share/vpp/api/*
diff --git a/extras/scripts/build_static_vppctl.sh b/extras/scripts/build_static_vppctl.sh
index 7ed2be24e82..eafd24c3748 100755
--- a/extras/scripts/build_static_vppctl.sh
+++ b/extras/scripts/build_static_vppctl.sh
@@ -1,27 +1,17 @@
#/bin/env bash
+set -eu
src=$(realpath $(dirname $0)/../..)/src
${CC:-cc} \
+ -Wall \
+ -Werror \
-O2 \
-flto \
-static \
+ -D STATIC_VPPCTL \
-I ${src} \
- ${src}/vppinfra/backtrace.c \
- ${src}/vppinfra/dlmalloc.c \
- ${src}/vppinfra/elf.c \
- ${src}/vppinfra/elf_clib.c \
- ${src}/vppinfra/error.c \
- ${src}/vppinfra/format.c \
- ${src}/vppinfra/hash.c \
- ${src}/vppinfra/mem.c \
- ${src}/vppinfra/mem_dlmalloc.c \
- ${src}/vppinfra/std-formats.c \
- ${src}/vppinfra/string.c \
- ${src}/vppinfra/socket.c \
- ${src}/vppinfra/vec.c \
- ${src}/vppinfra/unformat.c \
- ${src}/vppinfra/unix-misc.c \
- ${src}/vppinfra/linux/mem.c \
+ -g \
${src}/vpp/app/vppctl.c \
-o vppctl
+
diff --git a/extras/scripts/check_documentation.sh b/extras/scripts/check_documentation.sh
new file mode 100755
index 00000000000..2ab093dfbcb
--- /dev/null
+++ b/extras/scripts/check_documentation.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+# Copyright (c) 2021 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.
+
+set -eEo pipefail
+
+SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+WS_ROOT=$( realpath ${SCRIPTDIR}/../.. )
+
+function red () { printf "\e[0;31m$1\e[0m\n" ; }
+function green () { printf "\e[0;32m$1\e[0m\n" ; }
+
+find_linked_docs () {
+ find ${WS_ROOT}/docs -type l \
+ \( -name '*.rst' -o -name '*.md' \) \
+ -exec readlink -f {} \; | sort
+}
+
+find_excluded_docs () {
+ cat ${WS_ROOT}/docs/docsignore \
+ | grep -v '#' \
+ | sed s@^@${WS_ROOT}/@ \
+ | sort
+}
+
+find_linked_and_excluded_docs () {
+ cat <( find_linked_docs ) <( find_excluded_docs ) | sort
+}
+
+find_candidate_docs () {
+ find \
+ ${WS_ROOT}/src \
+ ${WS_ROOT}/test \
+ ${WS_ROOT}/extras \
+ -not -path "${WS_ROOT}/test/venv/*" \
+ \( -name '*.rst' -o -name '*.md' \) \
+ | sort
+}
+
+spellcheck () {
+ make -C ${WS_ROOT} docs-spell
+}
+
+if [ "x$(comm -13 <( find_linked_and_excluded_docs ) <( find_candidate_docs ))" != x ]; then
+ red "The following files need to be linked"
+ red "in the doc folder e.g. :"
+ red "$ cd vpp/docs/developer/plugins"
+ red "$ ln -s ../../../src/plugins/my_plugin/my_plugin.rst"
+ echo ""
+ cat <( comm -13 <( find_linked_and_excluded_docs ) <( find_candidate_docs ) )
+ exit 1
+fi
+spellcheck
+green "**********************************************"
+green "* VPP Docs Checkstyle Successfully Completed *"
+green "**********************************************"
diff --git a/extras/scripts/checkstyle.sh b/extras/scripts/checkstyle.sh
index f8a83f3ec9d..2b884f5f08b 100755
--- a/extras/scripts/checkstyle.sh
+++ b/extras/scripts/checkstyle.sh
@@ -18,8 +18,14 @@ set -eEo pipefail
CLANG_FORMAT_VER_REGEX='([0-9]+)\.[0-9]+\.[0-9]+'
CLANG_FORMAT_DIFF="/usr/share/clang/clang-format-diff.py"
-CLANG_FORMAT_VER=${CLANG_FORMAT_VER:-10}
+# TODO: Remove clang-format-${CLANG_FORMAT_VER} from 'make install-deps' when
+# CLANG_FORMAT_VER default value is upgraded
+CLANG_FORMAT_VER=${CLANG_FORMAT_VER:-11}
GIT_DIFF_ARGS="-U0 --no-color --relative HEAD~1"
+GIT_DIFF_EXCLUDE_LIST=(
+ ':!*.patch'
+ ':(exclude)*src/vppinfra/dlmalloc.*'
+)
CLANG_FORMAT_DIFF_ARGS="-style file -p1"
SUFFIX="-${CLANG_FORMAT_VER}"
@@ -58,9 +64,12 @@ fi
if command -v clang-format-diff${SUFFIX} &> /dev/null;
then
CLANG_FORMAT_DIFF=clang-format-diff${SUFFIX}
+elif command -v clang-format-diff.py &> /dev/null;
+then
+ CLANG_FORMAT_DIFF=clang-format-diff.py
elif command -v clang-format-diff &> /dev/null;
then
- CLANG_FORMAT=clang-format-diff
+ CLANG_FORMAT_DIFF=clang-format-diff
elif [ ! -f $CLANG_FORMAT_DIFF ] ;
then
echo "*******************************************************************"
@@ -71,7 +80,7 @@ then
fi
in=$(mktemp)
-git diff ${GIT_DIFF_ARGS} ':!*.patch' > ${in}
+git diff ${GIT_DIFF_ARGS} ${GIT_DIFF_EXCLUDE_LIST[@]} > ${in}
line_count=$(sed -n '/^+.*\*INDENT-O[NF][F]\{0,1\}\*/p' ${in} | wc -l)
if [ ${line_count} -gt 0 ] ; then
diff --git a/extras/scripts/crcchecker.py b/extras/scripts/crcchecker.py
index f3021c3c8b6..7dcdb681e18 100755
--- a/extras/scripts/crcchecker.py
+++ b/extras/scripts/crcchecker.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
-'''
+"""
crcchecker is a tool to used to enforce that .api messages do not change.
API files with a semantic version < 1.0.0 are ignored.
-'''
+"""
import sys
import os
@@ -14,98 +14,110 @@ from subprocess import run, PIPE, check_output, CalledProcessError
# pylint: disable=subprocess-run-check
-ROOTDIR = os.path.dirname(os.path.realpath(__file__)) + '/../..'
-APIGENBIN = f'{ROOTDIR}/src/tools/vppapigen/vppapigen.py'
+ROOTDIR = os.path.dirname(os.path.realpath(__file__)) + "/../.."
+APIGENBIN = f"{ROOTDIR}/src/tools/vppapigen/vppapigen.py"
def crc_from_apigen(revision, filename):
- '''Runs vppapigen with crc plugin returning a JSON object with CRCs for
- all APIs in filename'''
+ """Runs vppapigen with crc plugin returning a JSON object with CRCs for
+ all APIs in filename"""
if not revision and not os.path.isfile(filename):
- print(f'skipping: {filename}', file=sys.stderr)
+ print(f"skipping: {filename}", file=sys.stderr)
# Return <class 'set'> instead of <class 'dict'>
return {-1}
if revision:
- apigen = (f'{APIGENBIN} --git-revision {revision} --includedir src '
- f'--input {filename} CRC')
+ apigen = (
+ f"{APIGENBIN} --git-revision {revision} --includedir src "
+ f"--input {filename} CRC"
+ )
else:
- apigen = (f'{APIGENBIN} --includedir src --input {filename} CRC')
+ apigen = f"{APIGENBIN} --includedir src --input {filename} CRC"
returncode = run(apigen.split(), stdout=PIPE, stderr=PIPE)
if returncode.returncode == 2: # No such file
- print(f'skipping: {revision}:{filename} {returncode}', file=sys.stderr)
+ print(f"skipping: {revision}:{filename} {returncode}", file=sys.stderr)
return {}
if returncode.returncode != 0:
- print(f'vppapigen failed for {revision}:{filename} with '
- 'command\n {apigen}\n error: {rv}',
- returncode.stderr.decode('ascii'), file=sys.stderr)
+ print(
+ f"vppapigen failed for {revision}:{filename} with "
+ "command\n {apigen}\n error: {rv}",
+ returncode.stderr.decode("ascii"),
+ file=sys.stderr,
+ )
sys.exit(-2)
return json.loads(returncode.stdout)
def dict_compare(dict1, dict2):
- '''Compare two dictionaries returning added, removed, modified
- and equal entries'''
+ """Compare two dictionaries returning added, removed, modified
+ and equal entries"""
d1_keys = set(dict1.keys())
d2_keys = set(dict2.keys())
intersect_keys = d1_keys.intersection(d2_keys)
added = d1_keys - d2_keys
removed = d2_keys - d1_keys
- modified = {o: (dict1[o], dict2[o]) for o in intersect_keys
- if dict1[o]['crc'] != dict2[o]['crc']}
+ modified = {
+ o: (dict1[o], dict2[o])
+ for o in intersect_keys
+ if dict1[o]["crc"] != dict2[o]["crc"]
+ }
same = set(o for o in intersect_keys if dict1[o] == dict2[o])
return added, removed, modified, same
def filelist_from_git_ls():
- '''Returns a list of all api files in the git repository'''
+ """Returns a list of all api files in the git repository"""
filelist = []
- git_ls = 'git ls-files *.api'
+ git_ls = "git ls-files *.api"
returncode = run(git_ls.split(), stdout=PIPE, stderr=PIPE)
if returncode.returncode != 0:
sys.exit(returncode.returncode)
- for line in returncode.stdout.decode('ascii').split('\n'):
+ for line in returncode.stdout.decode("ascii").split("\n"):
if line:
filelist.append(line)
return filelist
def is_uncommitted_changes():
- '''Returns true if there are uncommitted changes in the repo'''
- git_status = 'git status --porcelain -uno'
- returncode = run(git_status.split(), stdout=PIPE, stderr=PIPE)
- if returncode.returncode != 0:
- sys.exit(returncode.returncode)
-
- if returncode.stdout:
- return True
+ """Returns true if there are uncommitted changes in the repo"""
+ # Don't run this check in the Jenkins CI
+ if os.getenv("FDIOTOOLS_IMAGE") is None:
+ git_status = "git status --porcelain -uno"
+ returncode = run(git_status.split(), stdout=PIPE, stderr=PIPE)
+ if returncode.returncode != 0:
+ sys.exit(returncode.returncode)
+
+ if returncode.stdout:
+ return True
return False
def filelist_from_git_grep(filename):
- '''Returns a list of api files that this <filename> api files imports.'''
+ """Returns a list of api files that this <filename> api files imports."""
filelist = []
try:
- returncode = check_output(f'git grep -e "import .*{filename}"'
- ' -- *.api',
- shell=True)
+ returncode = check_output(
+ f'git grep -e "import .*{filename}"' " -- *.api", shell=True
+ )
except CalledProcessError:
return []
- for line in returncode.decode('ascii').split('\n'):
+ for line in returncode.decode("ascii").split("\n"):
if line:
- filename, _ = line.split(':')
+ filename, _ = line.split(":")
filelist.append(filename)
return filelist
def filelist_from_patchset(pattern):
- '''Returns list of api files in changeset and the list of api
- files they import.'''
+ """Returns list of api files in changeset and the list of api
+ files they import."""
filelist = []
- git_cmd = ('((git diff HEAD~1.. --name-only;git ls-files -m) | '
- 'sort -u | grep "\\.api$")')
+ git_cmd = (
+ "((git diff HEAD~1.. --name-only;git ls-files -m) | "
+ 'sort -u | grep "\\.api$")'
+ )
try:
res = check_output(git_cmd, shell=True)
except CalledProcessError:
@@ -113,7 +125,7 @@ def filelist_from_patchset(pattern):
# Check for dependencies (imports)
imported_files = []
- for line in res.decode('ascii').split('\n'):
+ for line in res.decode("ascii").split("\n"):
if not line:
continue
if not re.search(pattern, line):
@@ -126,88 +138,91 @@ def filelist_from_patchset(pattern):
def is_deprecated(message):
- '''Given a message, return True if message is deprecated'''
- if 'options' in message:
- if 'deprecated' in message['options']:
+ """Given a message, return True if message is deprecated"""
+ if "options" in message:
+ if "deprecated" in message["options"]:
return True
# recognize the deprecated format
- if 'status' in message['options'] and \
- message['options']['status'] == 'deprecated':
+ if (
+ "status" in message["options"]
+ and message["options"]["status"] == "deprecated"
+ ):
print("WARNING: please use 'option deprecated;'")
return True
return False
def is_in_progress(message):
- '''Given a message, return True if message is marked as in_progress'''
- if 'options' in message:
- if 'in_progress' in message['options']:
+ """Given a message, return True if message is marked as in_progress"""
+ if "options" in message:
+ if "in_progress" in message["options"]:
return True
# recognize the deprecated format
- if 'status' in message['options'] and \
- message['options']['status'] == 'in_progress':
+ if (
+ "status" in message["options"]
+ and message["options"]["status"] == "in_progress"
+ ):
print("WARNING: please use 'option in_progress;'")
return True
return False
def report(new, old):
- '''Given a dictionary of new crcs and old crcs, print all the
+ """Given a dictionary of new crcs and old crcs, print all the
added, removed, modified, in-progress, deprecated messages.
- Return the number of backwards incompatible changes made.'''
+ Return the number of backwards incompatible changes made."""
# pylint: disable=too-many-branches
- new.pop('_version', None)
- old.pop('_version', None)
+ new.pop("_version", None)
+ old.pop("_version", None)
added, removed, modified, _ = dict_compare(new, old)
backwards_incompatible = 0
# print the full list of in-progress messages
# they should eventually either disappear of become supported
for k in new.keys():
- newversion = int(new[k]['version'])
+ newversion = int(new[k]["version"])
if newversion == 0 or is_in_progress(new[k]):
- print(f'in-progress: {k}')
+ print(f"in-progress: {k}")
for k in added:
- print(f'added: {k}')
+ print(f"added: {k}")
for k in removed:
- oldversion = int(old[k]['version'])
- if oldversion > 0 and not is_deprecated(old[k]) and not \
- is_in_progress(old[k]):
+ oldversion = int(old[k]["version"])
+ if oldversion > 0 and not is_deprecated(old[k]) and not is_in_progress(old[k]):
backwards_incompatible += 1
- print(f'removed: ** {k}')
+ print(f"removed: ** {k}")
else:
- print(f'removed: {k}')
+ print(f"removed: {k}")
for k in modified.keys():
- oldversion = int(old[k]['version'])
- newversion = int(new[k]['version'])
+ oldversion = int(old[k]["version"])
+ newversion = int(new[k]["version"])
if oldversion > 0 and not is_in_progress(old[k]):
backwards_incompatible += 1
- print(f'modified: ** {k}')
+ print(f"modified: ** {k}")
else:
- print(f'modified: {k}')
+ print(f"modified: {k}")
# check which messages are still there but were marked for deprecation
for k in new.keys():
- newversion = int(new[k]['version'])
+ newversion = int(new[k]["version"])
if newversion > 0 and is_deprecated(new[k]):
if k in old:
if not is_deprecated(old[k]):
- print(f'deprecated: {k}')
+ print(f"deprecated: {k}")
else:
- print(f'added+deprecated: {k}')
+ print(f"added+deprecated: {k}")
return backwards_incompatible
def check_patchset():
- '''Compare the changes to API messages in this changeset.
+ """Compare the changes to API messages in this changeset.
Ignores API files with version < 1.0.0.
Only considers API files located under the src directory in the repo.
- '''
- files = filelist_from_patchset('^src/')
- revision = 'HEAD~1'
+ """
+ files = filelist_from_patchset("^src/")
+ revision = "HEAD~1"
oldcrcs = {}
newcrcs = {}
@@ -216,7 +231,7 @@ def check_patchset():
_ = crc_from_apigen(None, filename)
# Ignore removed files
if isinstance(_, set) == 0:
- if isinstance(_, set) == 0 and _['_version']['major'] == '0':
+ if isinstance(_, set) == 0 and _["_version"]["major"] == "0":
continue
newcrcs.update(_)
@@ -225,27 +240,31 @@ def check_patchset():
backwards_incompatible = report(newcrcs, oldcrcs)
if backwards_incompatible:
# alert on changing production API
- print("crcchecker: Changing production APIs in an incompatible way",
- file=sys.stderr)
+ print(
+ "crcchecker: Changing production APIs in an incompatible way",
+ file=sys.stderr,
+ )
sys.exit(-1)
else:
- print('*' * 67)
- print('* VPP CHECKAPI SUCCESSFULLY COMPLETED')
- print('*' * 67)
+ print("*" * 67)
+ print("* VPP CHECKAPI SUCCESSFULLY COMPLETED")
+ print("*" * 67)
def main():
- '''Main entry point.'''
- parser = argparse.ArgumentParser(description='VPP CRC checker.')
- parser.add_argument('--git-revision',
- help='Git revision to compare against')
- parser.add_argument('--dump-manifest', action='store_true',
- help='Dump CRC for all messages')
- parser.add_argument('--check-patchset', action='store_true',
- help='Check patchset for backwards incompatbile changes')
- parser.add_argument('files', nargs='*')
- parser.add_argument('--diff', help='Files to compare (on filesystem)',
- nargs=2)
+ """Main entry point."""
+ parser = argparse.ArgumentParser(description="VPP CRC checker.")
+ parser.add_argument("--git-revision", help="Git revision to compare against")
+ parser.add_argument(
+ "--dump-manifest", action="store_true", help="Dump CRC for all messages"
+ )
+ parser.add_argument(
+ "--check-patchset",
+ action="store_true",
+ help="Check patchset for backwards incompatbile changes",
+ )
+ parser.add_argument("files", nargs="*")
+ parser.add_argument("--diff", help="Files to compare (on filesystem)", nargs=2)
args = parser.parse_args()
@@ -267,17 +286,16 @@ def main():
for filename in files:
crcs.update(crc_from_apigen(args.git_revision, filename))
for k, value in crcs.items():
- print(f'{k}: {value}')
+ print(f"{k}: {value}")
sys.exit(0)
# Find changes between current patchset and given revision (previous)
if args.check_patchset:
if args.git_revision:
- print('Argument git-revision ignored', file=sys.stderr)
+ print("Argument git-revision ignored", file=sys.stderr)
# Check there are no uncomitted changes
if is_uncommitted_changes():
- print('Please stash or commit changes in workspace',
- file=sys.stderr)
+ print("Please stash or commit changes in workspace", file=sys.stderr)
sys.exit(-1)
check_patchset()
sys.exit(0)
@@ -286,7 +304,7 @@ def main():
# Find changes between a given file and a revision
files = args.files if args.files else filelist_from_git_ls()
- revision = args.git_revision if args.git_revision else 'HEAD~1'
+ revision = args.git_revision if args.git_revision else "HEAD~1"
oldcrcs = {}
newcrcs = {}
@@ -299,13 +317,16 @@ def main():
if args.check_patchset:
if backwards_incompatible:
# alert on changing production API
- print("crcchecker: Changing production APIs in an incompatible way", file=sys.stderr)
+ print(
+ "crcchecker: Changing production APIs in an incompatible way",
+ file=sys.stderr,
+ )
sys.exit(-1)
else:
- print('*' * 67)
- print('* VPP CHECKAPI SUCCESSFULLY COMPLETED')
- print('*' * 67)
+ print("*" * 67)
+ print("* VPP CHECKAPI SUCCESSFULLY COMPLETED")
+ print("*" * 67)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/extras/scripts/list_api_changes.py b/extras/scripts/list_api_changes.py
index ac8cf477810..7433f542aaa 100755
--- a/extras/scripts/list_api_changes.py
+++ b/extras/scripts/list_api_changes.py
@@ -4,21 +4,21 @@ import fnmatch
import os
import subprocess
-starttag = 'v19.08-rc0'
-endtag = 'HEAD'
+starttag = "v19.08-rc0"
+endtag = "HEAD"
emit_md = True
apifiles = []
-for root, dirnames, filenames in os.walk('.'):
- for filename in fnmatch.filter(filenames, '*.api'):
+for root, dirnames, filenames in os.walk("."):
+ for filename in fnmatch.filter(filenames, "*.api"):
apifiles.append(os.path.join(root, filename))
for f in apifiles:
- commits = subprocess.check_output(['git', 'log',
- '--oneline', starttag + '..' + endtag,
- f])
+ commits = subprocess.check_output(
+ ["git", "log", "--oneline", starttag + ".." + endtag, f]
+ )
if commits:
- if f[0:2] == './':
+ if f[0:2] == "./":
f = f[2:]
if emit_md:
print("| @c %s ||" % f)
@@ -27,9 +27,10 @@ for f in apifiles:
parts = line.strip().split()
commit = parts[0]
message = b" ".join(parts[1:]).decode().replace("|", r"\|")
- print("| [%s](https://gerrit.fd.io/r/gitweb?"
- "p=vpp.git;a=commit;h=%s) | %s |" % (
- commit, commit, message))
+ print(
+ "| [%s](https://gerrit.fd.io/r/gitweb?"
+ "p=vpp.git;a=commit;h=%s) | %s |" % (commit, commit, message)
+ )
print()
else:
print(f)
diff --git a/extras/scripts/lsnet b/extras/scripts/lsnet
index ed590e533ac..0e882ad89be 100755
--- a/extras/scripts/lsnet
+++ b/extras/scripts/lsnet
@@ -1,20 +1,51 @@
#!/bin/bash
-echo "PCI Address MAC address Device Name Driver State Speed Port Type"
-echo "============ ================= ============== ========== ======== ========== ===================="
+declare -A IDS
+IDS["8086:10fb"]="82599ES PF"
+IDS["8086:1583"]="XL710 PF"
+IDS["8086:158b"]="XXV710 PF"
+IDS["8086:154c"]="XXV710 VF"
+IDS["8086:37d1"]="X722 PF"
+IDS["8086:37cd"]="X722 VF"
+IDS["8086:1889"]="AVF"
+IDS["8086:1593"]="E810 PF"
+IDS["15b3:1015"]="CX-4 Lx PF"
+IDS["15b3:1017"]="CX-5 PF"
+IDS["15b3:1019"]="CX-5 Ex PF"
-for f in /sys/class/net/*; do
- dev=$(basename ${f})
- if [ -e $f/device ] ; then
- dev=$(basename ${f})
- pci_addr=$(basename $(readlink $f/device))
- mac=$(cat $f/address)
- driver=$(basename $(readlink $f/device/driver))
- oper=$(cat $f/operstate)
- speed=$(sudo ethtool $dev | grep Speed | cut -d" " -f2)
- port=$(ethtool $dev 2> /dev/null | sed -ne 's/.*Port: \(.*\)/\1/p')
- printf "%-12s %-14s %-14s %-10s %-8s %-10s %-20s\n" $pci_addr $mac $dev $driver $oper $speed "$port"
- # ethtool $dev | grep Port:
- fi
-done
+echo "PCI Address N PCI-ID Driver MAC address Device Name State Speed Port Type"
+echo "============ = =========== ========== ================= ============== ===== ========== ===================="
+
+for d in /sys/bus/pci/devices/*; do
+ class=$(cat $d/class)
+ [ "${class}" == "0x020000" ] || continue
+ pci_addr=$(basename $d)
+ numa=$(cat $d/numa_node)
+ vid=$(cat $d/vendor | sed -e s/0x//g)
+ did=$(cat $d/device| sed -e s/0x//g)
+ pci_id=${IDS[$vid:$did]}
+ [ "$pci_id" == "" ] && pci_id="$vid:$did"
+
+ if [ -e $d/driver ] ; then
+ driver=$(basename $(readlink $d/driver))
+ else
+ driver=" "
+ fi
+ if [ -e $d/net ] ; then
+ dev=$(basename $(ls $d/net | head -1))
+ n=/sys/class/net/$dev
+ mac=$(cat $n/address)
+ oper=$(cat $n/operstate)
+# speed=$(sudo ethtool $dev | grep Speed | cut -d" " -f2)
+##port=$(ethtool $dev 2> /dev/null | sed -ne 's/.*Port: \(.*\)/\1/p')
+ else
+ dev=" "
+ mac=" "
+ oper=" "
+ speed=" "
+ port=" "
+ fi
+ printf "%-12s %s %-11s %-10s %-17s %-14s %-5s %-10s %s\n" \
+ $pci_addr $numa "$pci_id" $driver $mac $dev $oper $speed "$port"
+done
diff --git a/extras/scripts/vpp-review b/extras/scripts/vpp-review
new file mode 100755
index 00000000000..8ce4bd2d9c1
--- /dev/null
+++ b/extras/scripts/vpp-review
@@ -0,0 +1,240 @@
+#!/usr/bin/perl
+
+use Data::Dumper;
+use Env;
+use File::Glob ':glob';
+
+#
+# Use this script to determine the reviewers for a given commit, like so:
+#
+# ayourtch@ayourtch-lnx:~/vpp$ ./extras/scripts/vpp-review HEAD
+# commit 7ea63c597f82412b68b5a565df10e13669b1decb
+# Author: Andrew Yourtchenko <ayourtch@gmail.com>
+# Date: Wed Jul 14 22:44:05 2021 +0200
+#
+# misc: experimental script to get the list of the reviewers for a commit
+#
+# accepts zero or one argument (the commit hash), and outputs
+# the detected components, the component maintainers,
+# and the final suggested reviewer list
+#
+# Change-Id: Ief671fe837c6201bb11fd05d02af881822b0bb33
+# Type: docs
+# Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
+#
+# :000000 100755 000000000 635dde59f A extras/scripts/vpp-review
+#
+#
+# Components of files in the commit:
+# misc: extras/scripts/vpp-review
+#
+# Component misc maintainers:
+# vpp-dev Mailing List <vpp-dev@fd.io>
+#
+#
+# Final reviewer list:
+# vpp-dev Mailing List <vpp-dev@fd.io>
+# ayourtch@ayourtch-lnx:~/vpp$
+#
+
+#
+# Read the maintainers file into a hash, indexed by short component name.
+#
+sub read_maintainers() {
+ open(F, "MAINTAINERS") || die("Could not open MAINTAINERS file");
+ my $short_name = "";
+ my $long_name = "";
+ my $in_component = 0;
+ my $maintainers = [];
+ my $paths = [];
+ my $exclude_paths = [];
+ my $feature_yaml = [];
+ my $comments = [];
+ my $out = {};
+ while (<F>) {
+ chomp();
+ my $aLine = $_;
+ # print ("LINE: $aLine\n");
+ if (/^([A-Z]):\s+(.*)$/) {
+ my $aLetter = $1;
+ my $aValue = $2;
+ # print ("LETTER: $aLetter, VALUE: $aValue\n");
+ if ($aLetter eq "I") {
+ $short_name = $aValue;
+ }
+ elsif ($aLetter eq "M") {
+ push(@{$maintainers}, $aValue);
+ }
+ elsif ($aLetter eq "F") {
+ push(@{$paths}, $aValue);
+ }
+ elsif ($aLetter eq "E") {
+ push(@{$exclude_paths}, $aValue);
+ }
+ elsif ($aLetter eq "Y") {
+ push(@{$feature_yaml}, $aValue);
+ }
+ elsif ($aLetter eq "C") {
+ push(@{$comments}, $aValue);
+ } else {
+ print ("LETTER: $aLetter, VALUE: $aValue\n");
+ }
+ # FIXME: deal with all the other letters here
+ } elsif (/^(\s*)$/) {
+ if ($in_component) {
+ $in_component = 0;
+ if ($short_name ne "") {
+ my $comp = {};
+ $comp->{'short_name'} = $short_name;
+ $comp->{'name'} = $long_name;
+ $comp->{'maintainers'} = $maintainers;
+ $comp->{'paths'} = $paths;
+ $comp->{'comments'} = $comments;
+ $comp->{'exclude_paths'} = $exclude_paths;
+ $comp->{'feature_yaml'} = $feature_yaml;
+ $out->{$short_name} = $comp;
+ # print("FEATURE: $short_name => $long_name\n");
+ $short_name = "";
+ $long_name = "";
+ $maintainers = [];
+ $paths = [];
+ $exclude_paths = [];
+ $comments = [];
+ $feature_yaml = [];
+ }
+ }
+ # print ("END\n");
+ } elsif (/^([^\s].*)$/) {
+ $in_component = 1;
+ $long_name = $1;
+ }
+ }
+
+ if ($in_component) {
+ $in_component = 0;
+ if ($short_name ne "") {
+ my $comp = {};
+ $comp->{'short_name'} = $short_name;
+ $comp->{'name'} = $long_name;
+ $comp->{'maintainers'} = $maintainers;
+ $comp->{'paths'} = $paths;
+ $comp->{'comments'} = $comments;
+ $comp->{'exclude_paths'} = $exclude_paths;
+ $comp->{'feature_yaml'} = $feature_yaml;
+ $out->{$short_name} = $comp;
+ }
+
+ }
+
+ return($out);
+}
+
+sub match_my_path {
+ my $p = $_[0];
+ my $apath = $_[1];
+ my $root = $_[2];
+
+ my $p1 = $p;
+ $p1 =~ s#\*#[^/]*#g;
+
+ my $pattern = "$root$p1";
+
+ if ($apath =~ /$pattern/) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+sub match_path_to_list {
+ my $path_list = $_[0];
+ my $apath = $_[1];
+ my $root = $_[2];
+ foreach $p (@{$path_list}) {
+ if (match_my_path($p, $apath, $root)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub match_path_to_comp_hash {
+ my $chash = $_[0];
+ my $apath = $_[1];
+ my $root = $_[2];
+ my $is_included = match_path_to_list($chash->{'paths'}, $apath, $root);
+ my $is_excluded = match_path_to_list($chash->{'exclude_paths'}, $apath, $root);
+ return ($is_included && !$is_excluded);
+}
+
+
+sub match_path {
+ my $components = $_[0];
+ my $apath = $_[1];
+ my $root = $_[2];
+ my $out = [];
+
+ foreach $aCompName (keys %{$components}) {
+ my $chash = $components->{$aCompName};
+ if (match_path_to_comp_hash($chash, $apath, $root)) {
+ push(@{$out}, $aCompName);
+ }
+ }
+ my $out1 = $out;
+
+ if (scalar @{$out} > 1) {
+ # not very efficient way to filter out "misc" but oh well.
+ $out1 = [];
+ foreach $aval (@{$out}) {
+ if ($aval ne "misc") {
+ push(@{$out1}, $aval);
+ }
+ }
+ }
+
+ return ($out1);
+}
+
+my $commit_id = $ARGV[0];
+my $commit_log = `git log --raw -n 1 $commit_id`;
+my $files = [];
+foreach my $aLine (split(/[\r\n]+/, $commit_log)) {
+ if ($aLine =~ /^:[0-7]+\s+[0-7]+\s+[0-9a-f]+\s+[0-9a-f]+\s+\S+\s+(\S+)$/) {
+ push(@{$files}, $1);
+ }
+}
+
+my $comp = read_maintainers();
+my $matched_comps = {};
+my $matched_reviewers = {};
+
+print("$commit_log\n\n");
+
+print("Components of files in the commit:\n");
+
+foreach $aFile (@{$files}) {
+ my $matches = match_path($comp, $aFile, '');
+ my $matches_str = join(" ", @{$matches});
+ print (" $matches_str: $aFile\n");
+
+ foreach my $aComp (@{$matches}) {
+ $matched_comps->{$aComp} = 1;
+ }
+}
+
+foreach my $aKey (keys %{$matched_comps}) {
+ print("\nComponent $aKey maintainers:\n");
+ foreach my $aRV (@{$comp->{$aKey}->{'maintainers'}}) {
+ print(" $aRV\n");
+ $matched_reviewers->{$aRV} = 1;
+ }
+}
+
+print("\n\nFinal reviewer list:\n");
+
+foreach my $aRV (keys %{$matched_reviewers}) {
+ print(" $aRV\n");
+}
+# print Dumper(\$comp);
+
+
diff --git a/extras/selinux/selinux_doc.md b/extras/selinux/selinux_doc.md
deleted file mode 100644
index 2320dd538d2..00000000000
--- a/extras/selinux/selinux_doc.md
+++ /dev/null
@@ -1,506 +0,0 @@
-# SELinux - VPP Custom SELinux Policy {#selinux_doc}
-
-## Overview
-
-Security-enhanced Linux (SELinux) is a security feature in the Linux kernel. At
-a very high level, SELinux implements mandatory access controls (MAC), as
-opposed to discretionary access control (DAC) implemented in standard Linux. MAC
-defines how processes can interact with other system components (Files,
-Directories, Other Processes, Pipes, Sockets, Network Ports). Each system
-component is assigned a label, and then the SELinux Policy defines which labels
-and which actions on each label a process is able to perform. The VPP Custom
-SELinux Policy defines the actions VPP is allowed to perform on which labels.
-
-The VPP Custom SELinux Policy is intended to be installed on RPM based platforms
-(tested on CentOS 7 and RHEL 7). Though SELinux can run on Debian platforms, it
-typically is not and therefore is not currently being built for Debian.
-
-The VPP Custom SELinux Policy does not enable or disable SELinux, only allows
-VPP to run when SELinux is enabled. A fresh install of either Fedora, CentOS or
-RHEL will have SELinux enabled by default. To determine if SELinux is enabled on
-a given system and enable it if needed, run:
-
-```
- $ getenforce
- Permissive
-
- $ sudo setenforce 1
-
- $ getenforce
- Enforcing
-```
-
-To make the change persistent, modify the following file to set
-`SELINUX=enforcing`:
-
-```
- $ sudo vi /etc/selinux/config
- :
- # This file controls the state of SELinux on the system.
- # SELINUX= can take one of these three values:
- # enforcing - SELinux security policy is enforced.
- # permissive - SELinux prints warnings instead of enforcing.
- # disabled - No SELinux policy is loaded.
- SELINUX=enforcing
- :
-```
-
-## Installation
-
-To install VPP, see the installation instructions on the VPP Wiki
-(https://wiki.fd.io/view/VPP/Installing_VPP_binaries_from_packages). The VPP
-Custom SELinux Policy is packaged in its own RPM starting in 18.04,
-`vpp-selinux-policy-<VERSION>-<RELEASE>.rpm`. It is packaged and installed along
-with the other VPP RPMs.
-
-### Fresh Install of VPP
-
-If VPP has never been installed on a system, then starting in 18.04, the VPP
-Custom SELinux Policy will be installed with the other RPMs and all the system
-components managed by VPP will be labeled properly.
-
-### Fix SELinux Labels for VPP
-In the case where the VPP Custom Policy is being installed for the first time,
-either because VPP has been upgraded or packages were removed and then
-reinstalled, several directories and files will not not be properly labeled. The
-labels on these files will need to be fixed for VPP to run properly with SELinux
-enabled. After the VPP Custom SELinux Policy is installed, run the following
-commands to fix the labels. If VPP is already running, make sure to restart
-VPP after the labels are fixed. This change is persistent for the life of the
-file. Once the VPP Custom Policy is installed on the system, subsequent files
-created by VPP will be labeled properly. This is only to fix files created by
-VPP prior to the VPP Custom Policy being installed.
-
-```
- $ sudo restorecon -Rv /etc/vpp/
- $ sudo restorecon -Rv /usr/lib/vpp_api_test_plugins/
- $ sudo restorecon -Rv /usr/lib/vpp_plugins/
- $ sudo restorecon -Rv /usr/share/vpp/
- $ sudo restorecon -Rv /var/run/vpp/
-
- $ sudo chcon -t vpp_tmp_t /tmp/vpp_*
- $ sudo chcon -t vpp_var_run_t /var/run/.vpp_*
-```
-
-**NOTE:** Because the VPP APIs allow custom filenames in certain scenarios, the
-above commands may not handle all files. Inspect your system and correct any
-files that are mislabeled. For example, to verify all VPP files in `/tmp/` are
-labeled properly, run:
-
-```
- $ sudo ls -alZ /tmp/
-```
-
-Any files not properly labeled with `vpp_tmp_t`, run:
-
-```
- $ sudo chcon -t vpp_tmp_t /tmp/<filename>
-```
-
-## VPP Files
-
-### Recommended Default File Directories
-
-Documentation in the VPP Wiki (https://wiki.fd.io/view/VPP/) and doxygen
-generated documentation have examples with files located in certain directories.
-Some of the recommend file locations have been moved to satisfy SELinux. Most of
-the documentation has been updated, but links to older documentation still exist
-and there may have been instances that were missed. Use the file locations
-described below to allow SELinux to properly label the given files.
-
-File locations that have changed:
-* VPP Debug CLI Script Files
-* vHost Sockets
-* VPP Log Files
-
-#### VPP Debug CLI Script Files
-
-The VPP Debug CLI, `vppctl`, allows a sequence of CLI commands to be read from a
-file and executed. To avoid from having to grant VPP access to all of `/tmp/` and
-possibly `/home/` sub-directories, it is recommended that any VPP Debug CLI script
-files be placed in a common directory such as `/usr/share/vpp/`.
-
-For example:
-```
-$ cat /usr/share/vpp/scripts/gigup.txt
-set interface state GigabitEthernet0/8/0 up
-set interface state GigabitEthernet0/9/0 up
-```
-
-To execute:
-```
-$ vppctl exec /usr/share/vpp/scripts/gigup.txt
-```
-Or
-```
-$ vppctl
- _______ _ _ _____ ___
- __/ __/ _ \ (_)__ | | / / _ \/ _ \
- _/ _// // / / / _ \ | |/ / ___/ ___/
- /_/ /____(_)_/\___/ |___/_/ /_/
-
-vpp# exec /usr/share/vpp/scripts/gigup.txt
-vpp# quit
-
-```
-
-If the file is not labeled properly, you will see something similar to:
-```
-$ vppctl exec /home/<user>/dev/vpp/scripts/vppctl/gigup.txt
-exec: failed to open `/home/<user>/dev/vpp/scripts/vppctl/gigup.txt': Permission denied
-
-$ ls -alZ
-drwxrwxr-x. <user> <user> unconfined_u:object_r:user_home_t:s0 .
-drwxrwxr-x. <user> <user> unconfined_u:object_r:user_home_t:s0 ..
--rw-r--r--. <user> <user> unconfined_u:object_r:user_home_t:s0 gigup.txt
-```
-
-##### Original Documentation
-
-Some of the original documentation showed script files being executed out of
-`/tmp/`. Convenience also may lead to script files being placed in
-`/home/<user>/` subdirectories. If a file is generated by the VPP process in
-`/tmp/`, for example a trace file or pcap file, it will get properly labeled
-with the SELinux label `vpp_tmp_t`. When a file is created, unless a rule is in
-place for the process that created it, the file will inherit the SELinux label
-of the parent directory. So if a user creates a file themselves in `/tmp/`, it
-will get the SELinux label `tmp_t`, which VPP does not have permission to
-access. Therefore it is recommended that script files are located as described
-above.
-
-#### vHost Sockets
-
-vHost sockets are created from VPP perspective in either Server or Client mode.
-In Server mode, the socket name is provided to VPP and VPP creates the socket.
-In Client mode, the socket name is provided to VPP and the hypervisor creates
-the socket. In order for VPP and hypervisor to share the socket resource with
-SELinux enabled, a rule in the VPP Custom SELinux Policy has been added. This
-rules allows processes with the `svirt_t` label (the hypervisor) to access
-sockets with the `vpp_var_run_t` label. As such, when SELinux is enabled,
-vHost sockets should be created in the directory `/var/run/vpp/`.
-
-##### Original Documentation
-
-Some of the original documentation showed vHost sockets being created in the
-directory `/tmp/`. To work properly with SELinux enabled, vHost sockets should be
-created as described above.
-
-#### VPP Log Files
-
-The VPP log file location is set by updating the `/etc/vpp/startup.conf` file:
-
-```
-vi /etc/vpp/startup.conf
-unix {
-:
- log /var/log/vpp/vpp.log
-:
-}
-
-```
-
-By moving the log file to `/var/log/vpp/`, it will get the label `vpp_log_t`,
-which indicates that the files are log files so they benefit from the
-associated rules (for example granting rights to logrotate so that it can
-manipulate them).
-
-##### Original Documentation
-
-The default `startup.conf` file creates the VPP log file in `/tmp/vpp.log`. By
-leaving the log file in `/tmp/`, it will get the label `vpp_tmp_t`. Moving it
-to `/var/log/vpp/`, it will get the label `vpp_log_t`.
-
-### Use of Non-default File Directories
-
-VPP installs multiple files on the system.
-Some files have fixed directory and file names:
-- /etc/bash_completion.d/vppctl_completion
-- /etc/sysctl.d/80-vpp.conf
-- /usr/lib/systemd/system/vpp.service
-
-Others files have default directory and file names but the default can be
-overwritten:
-- /etc/vpp/startup.conf
- - Can be changed via the `/usr/lib/systemd/system/vpp.service` file by
- changing the -c option on the VPP command line:
-
-```
-ExecStart=/usr/bin/vpp -c /etc/vpp/startup.conf
-```
-
-- /run/vpp/cli.sock
- - Can be changed via the `/etc/vpp/startup.conf` file by changing the
- cli-listen setting:
-
-```
-unix {
-:
- cli-listen /run/vpp/cli.sock
-:
-}
-```
-
-
-- /var/log/vpp/vpp.log
- - Can be changed via the `/etc/vpp/startup.conf` file by changing the log
- setting:
-
-```
-unix {
- :
- log /var/log/vpp/vpp.log
- :
-}
-
-```
-
-If the directory of any VPP installed files is changed from the default, ensure
-that the proper SELiunx label is applied. The SELinux label can be determined by
-passing the -Z option to many common Linux commands:
-
-```
-ls -alZ /run/vpp/
-drwxr-xr-x. root vpp system_u:object_r:vpp_var_run_t:s0 .
-drwxr-xr-x. root root system_u:object_r:var_run_t:s0 ..
-srwxrwxr-x. root vpp system_u:object_r:vpp_var_run_t:s0 cli.sock
-```
-
-### VPP SELinux Types ###
-
-The following SELinux types are created by the VPP Custom SELinux Policy:
-- `vpp_t` - Applied to:
- - VPP process and spawned threads.
-
-- `vpp_config_rw_t` - Applied to:
- - `/etc/vpp/*`
-
-- `vpp_tmp_t` - Applied to:
- - `/tmp/*`
-
-- `vpp_exec_t` - Applied to:
- - `/usr/bin/*`
-
-- `vpp_lib_t` - Applied to:
- - `/usr/lib/vpp_api_test_plugins/*`
- - `/usr/lib/vpp_plugins/*`
-
-- `vpp_unit_file_t` - Applied to:
- - `/usr/lib/systemd/system/vpp.*`
-
-- `vpp_log_t` - Applied to:
- - `/var/log/vpp/*`
-
-- `vpp_var_run_t` - Applied to:
- - `/var/run/vpp/*`
-
-## Debug SELinux Issues
-
-If SELinux issues are suspected, there are a few steps that can be taken to
-debug the issue. This section provides a few pointers on on those steps. Any
-SELinux JIRAs will need this information to properly address the issue.
-
-### Additional SELinux Packages and Setup
-
-First, install the SELinux troubleshooting packages:
-
-```
-$ sudo yum -y install setroubleshoot setroubleshoot-server setools-console
--- OR --
-$ sudo dnf -y install setroubleshoot setroubleshoot-server setools-console
-```
-
-To enable proper logging, restart auditd:
-
-```
-$ sudo service auditd restart
-```
-
-While debugging issues, it is best to set SELinux to `Permissive` mode. In
-`Permissive` mode, SELinux will still detect and flag errors, but will allow
-processes to continue normal operation. This allows multiple errors to be
-collected at once as opposed to breaking on each individual error. To set
-SELinux to `Permissive` mode (until next reboot or it is set back), use:
-
-```
-$ sudo setenforce 0
-
-$ getenforce
-Permissive
-```
-
-After debugging, to set SELinux back to `Enforcing` mode, use:
-
-```
-$ sudo setenforce 1
-
-$ getenforce
-Enforcing
-```
-
-### Debugging
-
-Once the SELinux troubleshooting packages are installed, perform the actions
-that are suspected to be blocked by SELinux. Either `tail` the log during
-these actions or `grep` the log for additional SELinux logs:
-
-```
-sudo tail -f /var/log/messages
--- OR --
-sudo journalctl -f
-```
-
-Below are some examples of SELinux logs that are generated:
-
-```
-May 14 11:28:34 svr-22 setroubleshoot: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt. For complete SELinux messages run: sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
-May 14 11:28:34 svr-22 python: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp should be allowed read access on the hostCreate.txt file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
-May 14 11:28:34 svr-22 setroubleshoot: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt. For complete SELinux messages run: sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
-May 14 11:28:34 svr-22 python: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp should be allowed read access on the hostCreate.txt file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
-May 14 11:28:37 svr-22 setroubleshoot: SELinux is preventing vpp_main from map access on the packet_socket packet_socket. For complete SELinux messages run: sealert -l ab6667d9-3f14-4dbd-96a0-7a655f7b4eb1
-May 14 11:28:37 svr-22 python: SELinux is preventing vpp_main from map access on the packet_socket packet_socket.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp_main should be allowed map access on the packet_socket packet_socket by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
-May 14 11:28:51 svr-22 setroubleshoot: SELinux is preventing vpp_main from map access on the packet_socket packet_socket. For complete SELinux messages run: sealert -l ab6667d9-3f14-4dbd-96a0-7a655f7b4eb1
-May 14 11:28:51 svr-22 python: SELinux is preventing vpp_main from map access on the packet_socket packet_socket.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp_main should be allowed map access on the packet_socket packet_socket by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
-```
-
-From the logs above, there are two sets of commands that are recommended to be
-run. The first is to run the `sealert` command. The second is to run the
-`ausearch | audit2allow` commands and the `semodule` command.
-
-#### sealert Command
-
-This `sealert` command provides a more detailed output for the given issue
-detected.
-
-```
-$ sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
-SELinux is preventing /usr/bin/vpp from 'read, write' accesses on the chr_file noiommu-0.
-
-***** Plugin device (91.4 confidence) suggests ****************************
-
-If you want to allow vpp to have read write access on the noiommu-0 chr_file
-Then you need to change the label on noiommu-0 to a type of a similar device.
-Do
-# semanage fcontext -a -t SIMILAR_TYPE 'noiommu-0'
-# restorecon -v 'noiommu-0'
-
-***** Plugin catchall (9.59 confidence) suggests **************************
-
-If you believe that vpp should be allowed read write access on the noiommu-0 chr_file by default.
-Then you should report this as a bug.
-You can generate a local policy module to allow this access.
-Do
-allow this access for now by executing:
-# ausearch -c 'vpp' --raw | audit2allow -M my-vpp
-# semodule -i my-vpp.pp
-
-
-Additional Information:
-Source Context system_u:system_r:vpp_t:s0
-Target Context system_u:object_r:device_t:s0
-Target Objects noiommu-0 [ chr_file ]
-Source vpp
-Source Path /usr/bin/vpp
-Port <Unknown>
-Host vpp_centos7_selinux
-Source RPM Packages vpp-19.01.2-rc0~17_gcfd3086.x86_64
-Target RPM Packages
-Policy RPM selinux-policy-3.13.1-229.el7_6.12.noarch
-Selinux Enabled True
-Policy Type targeted
-Enforcing Mode Permissive
-Host Name vpp_centos7_selinux
-Platform Linux vpp_centos7_selinux
- 3.10.0-957.12.1.el7.x86_64 #1 SMP Mon Apr 29
- 14:59:59 UTC 2019 x86_64 x86_64
-Alert Count 1
-First Seen 2019-05-13 18:10:50 EDT
-Last Seen 2019-05-13 18:10:50 EDT
-Local ID a418f869-f470-4c8a-b8e9-bdd41f2dd60b
-
-Raw Audit Messages
-type=AVC msg=audit(1557785450.964:257): avc: denied { read write } for pid=5273 comm="vpp" name="noiommu-0" dev="devtmpfs" ino=36022 scontext=system_u:system_r:vpp_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=chr_file permissive=1
-
-
-type=AVC msg=audit(1557785450.964:257): avc: denied { open } for pid=5273 comm="vpp" path="/dev/vfio/noiommu-0" dev="devtmpfs" ino=36022 scontext=system_u:system_r:vpp_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=chr_file permissive=1
-
-
-type=SYSCALL msg=audit(1557785450.964:257): arch=x86_64 syscall=open success=yes exit=ENOTBLK a0=7fb395ffd7f0 a1=2 a2=7fb395ffd803 a3=7fb395ffe2a0 items=0 ppid=1 pid=5273 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=993 sgid=0 fsgid=993 tty=(none) ses=4294967295 comm=vpp exe=/usr/bin/vpp subj=system_u:system_r:vpp_t:s0 key=(null)
-
-Hash: vpp,vpp_t,device_t,chr_file,read,write
-```
-
-In general, this command pumps out too much info and is only needed for
-additional debugging for tougher issues. Also note that once the process being
-tested is restarted, this command loses it's context and will not provide any
-information:
-
-```
-$ sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
-Error
-query_alerts error (1003): id (a418f869-f470-4c8a-b8e9-bdd41f2dd60b) not found
-```
-
-#### ausearch | audit2allow and semodule Commands
-
-These set of commands are more useful for basic debugging. The
-`ausearch | audit2allow` commands generate a set files. It may be worthwhile to
-run the commands in a temporary subdirectory:
-
-```
-$ mkdir test-01/; cd test-01/
-
-$ sudo ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain
-
-$ ls
-my-vpp.pp my-vpp.te
-
-$ cat my-vpp.te
-module my-vpp 1.0;
-
-require {
- type user_home_t;
- type vpp_t;
- class packet_socket map;
- class file { open read };
-}
-
-#============= vpp_t ==============
-allow vpp_t self:packet_socket map;
-allow vpp_t user_home_t:file { open read };
-```
-
-As shown above, the file `my-vpp.te` has been generated. This file shows
-possible changes to the SELinux policy that may fix the issue. If an SELinux
-policy was being created from scratch, this policy could be applied using the
-`semodule -i my-vpp.pp` command. HOWEVER, VPP already has a policy in place. So
-these changes need to be incorporated into the existing policy. The VPP SELinux
-policy is located in the following files:
-
-```
-$ ls extras/selinux/
-selinux_doc.md vpp-custom.fc vpp-custom.if vpp-custom.te
-```
-
-In this example, `map` needs to be added to the `packet_socket` class. If the
-`vpp-custom.te` is examined (prior to this fix), then one would see that the
-`packet_socket` class is already defined and just needs to be updated:
-
-```
-$ vi extras/selinux/vpp-custom.te
-:
-allow vpp_t self:process { execmem execstack setsched signal }; # too benevolent
-allow vpp_t self:packet_socket { bind create setopt ioctl }; <---
-allow vpp_t self:tun_socket { create relabelto relabelfrom };
-:
-```
-
-Before blindly applying the changes proposed by the `ausearch | audit2allow`
-commands, try to determine what is being allowed by the policy and determine if
-this is desired, or if the code can be reworked to no longer require the
-suggested permission. In the `my-vpp.te` file from above, it is suggested to
-allow `vpp_t` (i.e. the VPP process) access to all files in the home directory
-(`allow vpp_t user_home_t:file { open read };`). This was because a
-`vppctl exec` command was executed calling a script located in the
-`/home/<user>/` directory. Once this script was run from the `/usr/share/vpp/`
-directory as described in a section above, these permissions were no longer
-needed.
diff --git a/extras/selinux/selinux_doc.rst b/extras/selinux/selinux_doc.rst
new file mode 100644
index 00000000000..a902ec675ce
--- /dev/null
+++ b/extras/selinux/selinux_doc.rst
@@ -0,0 +1,554 @@
+.. _selinux_doc:
+
+SELinux - VPP Custom SELinux Policy
+===================================
+
+Overview
+--------
+
+Security-enhanced Linux (SELinux) is a security feature in the Linux
+kernel. At a very high level, SELinux implements mandatory access
+controls (MAC), as opposed to discretionary access control (DAC)
+implemented in standard Linux. MAC defines how processes can interact
+with other system components (Files, Directories, Other Processes,
+Pipes, Sockets, Network Ports). Each system component is assigned a
+label, and then the SELinux Policy defines which labels and which
+actions on each label a process is able to perform. The VPP Custom
+SELinux Policy defines the actions VPP is allowed to perform on which
+labels.
+
+The VPP Custom SELinux Policy is intended to be installed on RPM based
+platforms (tested on CentOS 7 and RHEL 7). Though SELinux can run on
+Debian platforms, it typically is not and therefore is not currently
+being built for Debian.
+
+The VPP Custom SELinux Policy does not enable or disable SELinux, only
+allows VPP to run when SELinux is enabled. A fresh install of either
+Fedora, CentOS or RHEL will have SELinux enabled by default. To
+determine if SELinux is enabled on a given system and enable it if
+needed, run:
+
+::
+
+ $ getenforce
+ Permissive
+
+ $ sudo setenforce 1
+
+ $ getenforce
+ Enforcing
+
+To make the change persistent, modify the following file to set
+``SELINUX=enforcing``:
+
+::
+
+ $ sudo vi /etc/selinux/config
+ :
+ # This file controls the state of SELinux on the system.
+ # SELINUX= can take one of these three values:
+ # enforcing - SELinux security policy is enforced.
+ # permissive - SELinux prints warnings instead of enforcing.
+ # disabled - No SELinux policy is loaded.
+ SELINUX=enforcing
+ :
+
+Installation
+------------
+
+To install VPP, see the installation instructions on the VPP Wiki
+(https://wiki.fd.io/view/VPP/Installing_VPP_binaries_from_packages). The
+VPP Custom SELinux Policy is packaged in its own RPM starting in 18.04,
+``vpp-selinux-policy-<VERSION>-<RELEASE>.rpm``. It is packaged and
+installed along with the other VPP RPMs.
+
+Fresh Install of VPP
+~~~~~~~~~~~~~~~~~~~~
+
+If VPP has never been installed on a system, then starting in 18.04, the
+VPP Custom SELinux Policy will be installed with the other RPMs and all
+the system components managed by VPP will be labeled properly.
+
+Fix SELinux Labels for VPP
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the case where the VPP Custom Policy is being installed for the first
+time, either because VPP has been upgraded or packages were removed and
+then reinstalled, several directories and files will not not be properly
+labeled. The labels on these files will need to be fixed for VPP to run
+properly with SELinux enabled. After the VPP Custom SELinux Policy is
+installed, run the following commands to fix the labels. If VPP is
+already running, make sure to restart VPP after the labels are fixed.
+This change is persistent for the life of the file. Once the VPP Custom
+Policy is installed on the system, subsequent files created by VPP will
+be labeled properly. This is only to fix files created by VPP prior to
+the VPP Custom Policy being installed.
+
+::
+
+ $ sudo restorecon -Rv /etc/vpp/
+ $ sudo restorecon -Rv /usr/lib/vpp_api_test_plugins/
+ $ sudo restorecon -Rv /usr/lib/vpp_plugins/
+ $ sudo restorecon -Rv /usr/share/vpp/
+ $ sudo restorecon -Rv /var/run/vpp/
+
+ $ sudo chcon -t vpp_tmp_t /tmp/vpp_*
+ $ sudo chcon -t vpp_var_run_t /var/run/.vpp_*
+
+**NOTE:** Because the VPP APIs allow custom filenames in certain
+scenarios, the above commands may not handle all files. Inspect your
+system and correct any files that are mislabeled. For example, to verify
+all VPP files in ``/tmp/`` are labeled properly, run:
+
+::
+
+ $ sudo ls -alZ /tmp/
+
+Any files not properly labeled with ``vpp_tmp_t``, run:
+
+::
+
+ $ sudo chcon -t vpp_tmp_t /tmp/<filename>
+
+VPP Files
+---------
+
+Recommended Default File Directories
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Documentation in the VPP Wiki (https://wiki.fd.io/view/VPP/) and doxygen
+generated documentation have examples with files located in certain
+directories. Some of the recommend file locations have been moved to
+satisfy SELinux. Most of the documentation has been updated, but links
+to older documentation still exist and there may have been instances
+that were missed. Use the file locations described below to allow
+SELinux to properly label the given files.
+
+File locations that have changed: \* VPP Debug CLI Script Files \* vHost
+Sockets \* VPP Log Files
+
+VPP Debug CLI Script Files
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The VPP Debug CLI, ``vppctl``, allows a sequence of CLI commands to be
+read from a file and executed. To avoid from having to grant VPP access
+to all of ``/tmp/`` and possibly ``/home/`` sub-directories, it is
+recommended that any VPP Debug CLI script files be placed in a common
+directory such as ``/usr/share/vpp/``.
+
+For example:
+
+::
+
+ $ cat /usr/share/vpp/scripts/gigup.txt
+ set interface state GigabitEthernet0/8/0 up
+ set interface state GigabitEthernet0/9/0 up
+
+To execute:
+
+::
+
+ $ vppctl exec /usr/share/vpp/scripts/gigup.txt
+
+Or
+
+::
+
+ $ vppctl
+ _______ _ _ _____ ___
+ __/ __/ _ \ (_)__ | | / / _ \/ _ \
+ _/ _// // / / / _ \ | |/ / ___/ ___/
+ /_/ /____(_)_/\___/ |___/_/ /_/
+
+ vpp# exec /usr/share/vpp/scripts/gigup.txt
+ vpp# quit
+
+If the file is not labeled properly, you will see something similar to:
+
+::
+
+ $ vppctl exec /home/<user>/dev/vpp/scripts/vppctl/gigup.txt
+ exec: failed to open `/home/<user>/dev/vpp/scripts/vppctl/gigup.txt': Permission denied
+
+ $ ls -alZ
+ drwxrwxr-x. <user> <user> unconfined_u:object_r:user_home_t:s0 .
+ drwxrwxr-x. <user> <user> unconfined_u:object_r:user_home_t:s0 ..
+ -rw-r--r--. <user> <user> unconfined_u:object_r:user_home_t:s0 gigup.txt
+
+Original Documentation
+''''''''''''''''''''''
+
+Some of the original documentation showed script files being executed
+out of ``/tmp/``. Convenience also may lead to script files being placed
+in ``/home/<user>/`` subdirectories. If a file is generated by the VPP
+process in ``/tmp/``, for example a trace file or pcap file, it will get
+properly labeled with the SELinux label ``vpp_tmp_t``. When a file is
+created, unless a rule is in place for the process that created it, the
+file will inherit the SELinux label of the parent directory. So if a
+user creates a file themselves in ``/tmp/``, it will get the SELinux
+label ``tmp_t``, which VPP does not have permission to access. Therefore
+it is recommended that script files are located as described above.
+
+vHost Sockets
+^^^^^^^^^^^^^
+
+vHost sockets are created from VPP perspective in either Server or
+Client mode. In Server mode, the socket name is provided to VPP and VPP
+creates the socket. In Client mode, the socket name is provided to VPP
+and the hypervisor creates the socket. In order for VPP and hypervisor
+to share the socket resource with SELinux enabled, a rule in the VPP
+Custom SELinux Policy has been added. This rules allows processes with
+the ``svirt_t`` label (the hypervisor) to access sockets with the
+``vpp_var_run_t`` label. As such, when SELinux is enabled, vHost sockets
+should be created in the directory ``/var/run/vpp/``.
+
+.. _original-documentation-1:
+
+Original Documentation
+''''''''''''''''''''''
+
+Some of the original documentation showed vHost sockets being created in
+the directory ``/tmp/``. To work properly with SELinux enabled, vHost
+sockets should be created as described above.
+
+VPP Log Files
+^^^^^^^^^^^^^
+
+The VPP log file location is set by updating the
+``/etc/vpp/startup.conf`` file:
+
+::
+
+ vi /etc/vpp/startup.conf
+ unix {
+ :
+ log /var/log/vpp/vpp.log
+ :
+ }
+
+By moving the log file to ``/var/log/vpp/``, it will get the label
+``vpp_log_t``, which indicates that the files are log files so they
+benefit from the associated rules (for example granting rights to
+logrotate so that it can manipulate them).
+
+.. _original-documentation-2:
+
+Original Documentation
+''''''''''''''''''''''
+
+The default ``startup.conf`` file creates the VPP log file in
+``/tmp/vpp.log``. By leaving the log file in ``/tmp/``, it will get the
+label ``vpp_tmp_t``. Moving it to ``/var/log/vpp/``, it will get the
+label ``vpp_log_t``.
+
+Use of Non-default File Directories
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+VPP installs multiple files on the system. Some files have fixed
+directory and file names: - /etc/bash_completion.d/vppctl_completion -
+/etc/sysctl.d/80-vpp.conf - /usr/lib/systemd/system/vpp.service
+
+Others files have default directory and file names but the default can
+be overwritten: - /etc/vpp/startup.conf - Can be changed via the
+``/usr/lib/systemd/system/vpp.service`` file by changing the -c option
+on the VPP command line:
+
+::
+
+ ExecStart=/usr/bin/vpp -c /etc/vpp/startup.conf
+
+- /run/vpp/cli.sock
+
+ - Can be changed via the ``/etc/vpp/startup.conf`` file by changing
+ the cli-listen setting:
+
+::
+
+ unix {
+ :
+ cli-listen /run/vpp/cli.sock
+ :
+ }
+
+- /var/log/vpp/vpp.log
+
+ - Can be changed via the ``/etc/vpp/startup.conf`` file by changing
+ the log setting:
+
+::
+
+ unix {
+ :
+ log /var/log/vpp/vpp.log
+ :
+ }
+
+If the directory of any VPP installed files is changed from the default,
+ensure that the proper SELiunx label is applied. The SELinux label can
+be determined by passing the -Z option to many common Linux commands:
+
+::
+
+ ls -alZ /run/vpp/
+ drwxr-xr-x. root vpp system_u:object_r:vpp_var_run_t:s0 .
+ drwxr-xr-x. root root system_u:object_r:var_run_t:s0 ..
+ srwxrwxr-x. root vpp system_u:object_r:vpp_var_run_t:s0 cli.sock
+
+VPP SELinux Types
+~~~~~~~~~~~~~~~~~
+
+The following SELinux types are created by the VPP Custom SELinux
+Policy: - ``vpp_t`` - Applied to: - VPP process and spawned threads.
+
+- ``vpp_config_rw_t`` - Applied to:
+
+ - ``/etc/vpp/*``
+
+- ``vpp_tmp_t`` - Applied to:
+
+ - ``/tmp/*``
+
+- ``vpp_exec_t`` - Applied to:
+
+ - ``/usr/bin/*``
+
+- ``vpp_lib_t`` - Applied to:
+
+ - ``/usr/lib/vpp_api_test_plugins/*``
+ - ``/usr/lib/vpp_plugins/*``
+
+- ``vpp_unit_file_t`` - Applied to:
+
+ - ``/usr/lib/systemd/system/vpp.*``
+
+- ``vpp_log_t`` - Applied to:
+
+ - ``/var/log/vpp/*``
+
+- ``vpp_var_run_t`` - Applied to:
+
+ - ``/var/run/vpp/*``
+
+Debug SELinux Issues
+--------------------
+
+If SELinux issues are suspected, there are a few steps that can be taken
+to debug the issue. This section provides a few pointers on on those
+steps. Any SELinux JIRAs will need this information to properly address
+the issue.
+
+Additional SELinux Packages and Setup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+First, install the SELinux troubleshooting packages:
+
+::
+
+ $ sudo yum -y install setroubleshoot setroubleshoot-server setools-console
+ -- OR --
+ $ sudo dnf -y install setroubleshoot setroubleshoot-server setools-console
+
+To enable proper logging, restart auditd:
+
+::
+
+ $ sudo service auditd restart
+
+While debugging issues, it is best to set SELinux to ``Permissive``
+mode. In ``Permissive`` mode, SELinux will still detect and flag errors,
+but will allow processes to continue normal operation. This allows
+multiple errors to be collected at once as opposed to breaking on each
+individual error. To set SELinux to ``Permissive`` mode (until next
+reboot or it is set back), use:
+
+::
+
+ $ sudo setenforce 0
+
+ $ getenforce
+ Permissive
+
+After debugging, to set SELinux back to ``Enforcing`` mode, use:
+
+::
+
+ $ sudo setenforce 1
+
+ $ getenforce
+ Enforcing
+
+Debugging
+~~~~~~~~~
+
+Once the SELinux troubleshooting packages are installed, perform the
+actions that are suspected to be blocked by SELinux. Either ``tail`` the
+log during these actions or ``grep`` the log for additional SELinux
+logs:
+
+::
+
+ sudo tail -f /var/log/messages
+ -- OR --
+ sudo journalctl -f
+
+Below are some examples of SELinux logs that are generated:
+
+::
+
+ May 14 11:28:34 svr-22 setroubleshoot: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt. For complete SELinux messages run: sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
+ May 14 11:28:34 svr-22 python: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp should be allowed read access on the hostCreate.txt file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
+ May 14 11:28:34 svr-22 setroubleshoot: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt. For complete SELinux messages run: sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
+ May 14 11:28:34 svr-22 python: SELinux is preventing /usr/bin/vpp from read access on the file hostCreate.txt.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp should be allowed read access on the hostCreate.txt file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
+ May 14 11:28:37 svr-22 setroubleshoot: SELinux is preventing vpp_main from map access on the packet_socket packet_socket. For complete SELinux messages run: sealert -l ab6667d9-3f14-4dbd-96a0-7a655f7b4eb1
+ May 14 11:28:37 svr-22 python: SELinux is preventing vpp_main from map access on the packet_socket packet_socket.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp_main should be allowed map access on the packet_socket packet_socket by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
+ May 14 11:28:51 svr-22 setroubleshoot: SELinux is preventing vpp_main from map access on the packet_socket packet_socket. For complete SELinux messages run: sealert -l ab6667d9-3f14-4dbd-96a0-7a655f7b4eb1
+ May 14 11:28:51 svr-22 python: SELinux is preventing vpp_main from map access on the packet_socket packet_socket.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that vpp_main should be allowed map access on the packet_socket packet_socket by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain#012# semodule -i my-vppmain.pp#012
+
+From the logs above, there are two sets of commands that are recommended
+to be run. The first is to run the ``sealert`` command. The second is to
+run the ``ausearch | audit2allow`` commands and the ``semodule``
+command.
+
+sealert Command
+^^^^^^^^^^^^^^^
+
+This ``sealert`` command provides a more detailed output for the given
+issue detected.
+
+::
+
+ $ sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
+ SELinux is preventing /usr/bin/vpp from 'read, write' accesses on the chr_file noiommu-0.
+
+ ***** Plugin device (91.4 confidence) suggests ****************************
+
+ If you want to allow vpp to have read write access on the noiommu-0 chr_file
+ Then you need to change the label on noiommu-0 to a type of a similar device.
+ Do
+ # semanage fcontext -a -t SIMILAR_TYPE 'noiommu-0'
+ # restorecon -v 'noiommu-0'
+
+ ***** Plugin catchall (9.59 confidence) suggests **************************
+
+ If you believe that vpp should be allowed read write access on the noiommu-0 chr_file by default.
+ Then you should report this as a bug.
+ You can generate a local policy module to allow this access.
+ Do
+ allow this access for now by executing:
+ # ausearch -c 'vpp' --raw | audit2allow -M my-vpp
+ # semodule -i my-vpp.pp
+
+
+ Additional Information:
+ Source Context system_u:system_r:vpp_t:s0
+ Target Context system_u:object_r:device_t:s0
+ Target Objects noiommu-0 [ chr_file ]
+ Source vpp
+ Source Path /usr/bin/vpp
+ Port <Unknown>
+ Host vpp_centos7_selinux
+ Source RPM Packages vpp-19.01.2-rc0~17_gcfd3086.x86_64
+ Target RPM Packages
+ Policy RPM selinux-policy-3.13.1-229.el7_6.12.noarch
+ Selinux Enabled True
+ Policy Type targeted
+ Enforcing Mode Permissive
+ Host Name vpp_centos7_selinux
+ Platform Linux vpp_centos7_selinux
+ 3.10.0-957.12.1.el7.x86_64 #1 SMP Mon Apr 29
+ 14:59:59 UTC 2019 x86_64 x86_64
+ Alert Count 1
+ First Seen 2019-05-13 18:10:50 EDT
+ Last Seen 2019-05-13 18:10:50 EDT
+ Local ID a418f869-f470-4c8a-b8e9-bdd41f2dd60b
+
+ Raw Audit Messages
+ type=AVC msg=audit(1557785450.964:257): avc: denied { read write } for pid=5273 comm="vpp" name="noiommu-0" dev="devtmpfs" ino=36022 scontext=system_u:system_r:vpp_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=chr_file permissive=1
+
+
+ type=AVC msg=audit(1557785450.964:257): avc: denied { open } for pid=5273 comm="vpp" path="/dev/vfio/noiommu-0" dev="devtmpfs" ino=36022 scontext=system_u:system_r:vpp_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=chr_file permissive=1
+
+
+ type=SYSCALL msg=audit(1557785450.964:257): arch=x86_64 syscall=open success=yes exit=ENOTBLK a0=7fb395ffd7f0 a1=2 a2=7fb395ffd803 a3=7fb395ffe2a0 items=0 ppid=1 pid=5273 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=993 sgid=0 fsgid=993 tty=(none) ses=4294967295 comm=vpp exe=/usr/bin/vpp subj=system_u:system_r:vpp_t:s0 key=(null)
+
+ Hash: vpp,vpp_t,device_t,chr_file,read,write
+
+In general, this command pumps out too much info and is only needed for
+additional debugging for tougher issues. Also note that once the process
+being tested is restarted, this command loses it’s context and will not
+provide any information:
+
+::
+
+ $ sealert -l a418f869-f470-4c8a-b8e9-bdd41f2dd60b
+ Error
+ query_alerts error (1003): id (a418f869-f470-4c8a-b8e9-bdd41f2dd60b) not found
+
+ausearch \| audit2allow and semodule Commands
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+These set of commands are more useful for basic debugging. The
+``ausearch | audit2allow`` commands generate a set files. It may be
+worthwhile to run the commands in a temporary subdirectory:
+
+::
+
+ $ mkdir test-01/; cd test-01/
+
+ $ sudo ausearch -c 'vpp_main' --raw | audit2allow -M my-vppmain
+
+ $ ls
+ my-vpp.pp my-vpp.te
+
+ $ cat my-vpp.te
+ module my-vpp 1.0;
+
+ require {
+ type user_home_t;
+ type vpp_t;
+ class packet_socket map;
+ class file { open read };
+ }
+
+ #============= vpp_t ==============
+ allow vpp_t self:packet_socket map;
+ allow vpp_t user_home_t:file { open read };
+
+As shown above, the file ``my-vpp.te`` has been generated. This file
+shows possible changes to the SELinux policy that may fix the issue. If
+an SELinux policy was being created from scratch, this policy could be
+applied using the ``semodule -i my-vpp.pp`` command. HOWEVER, VPP
+already has a policy in place. So these changes need to be incorporated
+into the existing policy. The VPP SELinux policy is located in the
+following files:
+
+::
+
+ $ ls extras/selinux/
+ selinux_doc.md vpp-custom.fc vpp-custom.if vpp-custom.te
+
+In this example, ``map`` needs to be added to the ``packet_socket``
+class. If the ``vpp-custom.te`` is examined (prior to this fix), then
+one would see that the ``packet_socket`` class is already defined and
+just needs to be updated:
+
+::
+
+ $ vi extras/selinux/vpp-custom.te
+ :
+ allow vpp_t self:process { execmem execstack setsched signal }; # too benevolent
+ allow vpp_t self:packet_socket { bind create setopt ioctl }; <---
+ allow vpp_t self:tun_socket { create relabelto relabelfrom };
+ :
+
+Before blindly applying the changes proposed by the
+``ausearch | audit2allow`` commands, try to determine what is being
+allowed by the policy and determine if this is desired, or if the code
+can be reworked to no longer require the suggested permission. In the
+``my-vpp.te`` file from above, it is suggested to allow ``vpp_t``
+(i.e. the VPP process) access to all files in the home directory
+(``allow vpp_t user_home_t:file { open read };``). This was because a
+``vppctl exec`` command was executed calling a script located in the
+``/home/<user>/`` directory. Once this script was run from the
+``/usr/share/vpp/`` directory as described in a section above, these
+permissions were no longer needed.
diff --git a/extras/selinux/vpp-custom.te b/extras/selinux/vpp-custom.te
index 6f183f687ad..27abbf92f85 100644
--- a/extras/selinux/vpp-custom.te
+++ b/extras/selinux/vpp-custom.te
@@ -10,6 +10,8 @@ gen_require(`
type svirt_t;
type svirt_image_t;
type systemd_sysctl_t;
+ type hugetlbfs_t;
+ type sysfs_t;
class capability sys_admin;
')
@@ -56,7 +58,7 @@ allow vpp_t self:netlink_socket { bind create setopt };
manage_dirs_pattern(vpp_t, vpp_lib_t, vpp_lib_t)
manage_files_pattern(vpp_t, vpp_lib_t, vpp_lib_t)
-allow vpp_t vpp_lib_t:file execute;
+allow vpp_t vpp_lib_t:file { execute map };
files_var_lib_filetrans(vpp_t, vpp_lib_t, {file dir})
manage_dirs_pattern(vpp_t, vpp_log_t, vpp_log_t)
@@ -77,6 +79,7 @@ files_tmp_filetrans(vpp_t, vpp_tmp_t, { dir sock_file file })
manage_dirs_pattern(vpp_t, vpp_tmpfs_t, vpp_tmpfs_t)
manage_files_pattern(vpp_t, vpp_tmpfs_t, vpp_tmpfs_t)
+allow vpp_t vpp_tmpfs_t:file map;
fs_tmpfs_filetrans(vpp_t, vpp_tmpfs_t, { dir file })
read_files_pattern(vpp_t, vpp_config_rw_t, vpp_config_rw_t)
@@ -141,4 +144,16 @@ allow vpp_t svirt_image_t:file { read write };
read_files_pattern(systemd_sysctl_t, vpp_config_rw_t, vpp_config_rw_t)
+########################################
+#
+# hugetlbfs
+#
+
+allow vpp_t hugetlbfs_t:file map;
+
+########################################
+#
+# dpdk
+#
+allow vpp_t sysfs_t:file map;
diff --git a/extras/snap/README.md b/extras/snap/README.md
deleted file mode 100644
index 47fdd3c204e..00000000000
--- a/extras/snap/README.md
+++ /dev/null
@@ -1,86 +0,0 @@
-VPP Snap Build {#snap_doc}
---------------
-
-General
--------
-
-The external dependency package will not build in the snapcraft
-vm. The path of least resistance is to copy it to the root of the
-(original) workspace before running the prep script.
-
-Snapcraft has mount issues except under /home. Run the prep script and
-copy the entire directory (including the .tgz file) under
-/home/yourself.
-
-Run the prep script
--------------------
-
-```
- $ cd <vpp-workspace>/extras/snap
- $ ./prep
-```
-
-Copy data to /home (if necessary)
-
-```
- $ mkdir /home/xxx
- $ cd <vpp-workspace>/extras/snap
- $ cp * /home/xxx
-
-Set snapcraft environment variables
------------------------------------
-
-Minimum requirements:
-
-```
- SNAPCRAFT_BUILD_ENVIRONMENT_MEMORY=16G
- SNAPCRAFT_BUILD_ENVIRONMENT_DISK=32G
-```
-
-Optional:
-
-```
- SNAPCRAFT_BUILD_ENVIRONMENT_CPU=8
- SNAPCRAFT_ENABLE_DEVELOPER_DEBUG=yes
-```
-
-Run snapcraft
--------------
-
-With luck, simply running snapcraft will produce the snap
-
-```
- $ <environment-variable-settings> snapcraft [--debug]
-```
-
-Rerunning snapcraft phases
---------------------------
-
-Here's how to (re)run individual phases, to avoid starting from
-scratch N times in case of errors:
-
-```
- snapcraft pull [<part-name>]
- snapcraft build [<part-name>]
- snapcraft stage [<part-name>]
- snapcraft prime [<part-name>]
- snapcraft snap or snapcraft
-```
-
-Restart without rebuilding VM
------------------------------
-
-To restart from scratch without rebuilding the VM:
-
-```
- snapcraft clean vpp
-```
-
-Delete (all) snapcraft VMs
---------------------------
-
-```
- for vm in $(multipass list | awk '{print $1}' | grep ^snapcraft-); do
- multipass delete $vm --purge
- done
-```
diff --git a/extras/snap/README.rst b/extras/snap/README.rst
new file mode 100644
index 00000000000..1a455565814
--- /dev/null
+++ b/extras/snap/README.rst
@@ -0,0 +1,84 @@
+.. _snap_doc:
+
+VPP Snap Build
+==============
+
+The external dependency package will not build in the snapcraft vm. The
+path of least resistance is to copy it to the root of the (original)
+workspace before running the prep script.
+
+Snapcraft has mount issues except under /home. Run the prep script and
+copy the entire directory (including the .tgz file) under
+/home/yourself.
+
+Run the prep script
+-------------------
+
+::
+
+ $ cd <vpp-workspace>/extras/snap
+ $ ./prep
+
+Copy data to /home (if necessary)
+
+::
+
+ $ mkdir /home/xxx
+ $ cd <vpp-workspace>/extras/snap
+ $ cp * /home/xxx
+
+ Set snapcraft environment variables
+ -----------------------------------
+
+ Minimum requirements:
+
+SNAPCRAFT_BUILD_ENVIRONMENT_MEMORY=16G
+SNAPCRAFT_BUILD_ENVIRONMENT_DISK=32G
+
+::
+
+
+ Optional:
+
+SNAPCRAFT_BUILD_ENVIRONMENT_CPU=8 SNAPCRAFT_ENABLE_DEVELOPER_DEBUG=yes
+
+::
+
+
+ Run snapcraft
+ -------------
+
+ With luck, simply running snapcraft will produce the snap
+
+$ snapcraft [–debug]
+
+::
+
+
+ Rerunning snapcraft phases
+ --------------------------
+
+ Here's how to (re)run individual phases, to avoid starting from
+ scratch N times in case of errors:
+
+snapcraft pull [] snapcraft build [] snapcraft stage [] snapcraft prime
+[] snapcraft snap or snapcraft
+
+::
+
+
+ Restart without rebuilding VM
+ -----------------------------
+
+ To restart from scratch without rebuilding the VM:
+
+snapcraft clean vpp
+
+::
+
+
+ Delete (all) snapcraft VMs
+ --------------------------
+
+for vm in $(multipass list \| awk ‘{print $1}’ \| grep ^snapcraft-); do
+multipass delete $vm –purge done \``\`
diff --git a/extras/strongswan/README.md b/extras/strongswan/README.md
deleted file mode 100644
index 48d02244370..00000000000
--- a/extras/strongswan/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# VPP Swanstrong Testing Recipe {#strongswan_test_doc}
-
-Simple test framework for VPP and strongSwan scenarios.
-
-## setup and run
-
-`docker` is needed to run the tests.
-
-Create `~/.vpp_sswan` file and set `VPP_BIN` and `VPPCTL` variables that points to vpp and vppctl binaries, like follows:
-```
-export VPP_BIN=/path/to/vpp
-export VPPCTL=/path/to/vppctl
-```
-
-To run all test
-```
-./run.sh
-```
-
-or specific test
-```
-./test_responder.sh
-```
diff --git a/extras/strongswan/README.rst b/extras/strongswan/README.rst
new file mode 100644
index 00000000000..2ba6f6e430f
--- /dev/null
+++ b/extras/strongswan/README.rst
@@ -0,0 +1,31 @@
+.. _strongswan_test_doc:
+
+Strongswan Testing Tool
+=======================
+
+Simple test framework for VPP and strongSwan scenarios.
+
+setup and run
+-------------
+
+``docker`` is needed to run the tests.
+
+Create ``~/.vpp_sswan`` file and set ``VPP_BIN`` and ``VPPCTL``
+variables that points to vpp and vppctl binaries, like follows:
+
+::
+
+ export VPP_BIN=/path/to/vpp
+ export VPPCTL=/path/to/vppctl
+
+To run all test
+
+::
+
+ ./run.sh
+
+or specific test
+
+::
+
+ ./test_responder.sh
diff --git a/extras/strongswan/configs/responder_nat/vpp.conf b/extras/strongswan/configs/responder_nat/vpp.conf
index 9ba3fdf2604..34c4f8fb704 100644
--- a/extras/strongswan/configs/responder_nat/vpp.conf
+++ b/extras/strongswan/configs/responder_nat/vpp.conf
@@ -32,7 +32,7 @@ ikev2 profile set pr1 tunnel ipip0
ip route add table 1 192.168.5.0/24 via ipip0
set interface unnumbered ipip0 use pipe0.1
-nat44 enable sessions 10
+nat44 plugin enable
nat44 add address 192.168.10.2
set interface nat44 in pipe0.0 out host-vpp
nat44 add static mapping udp local 10.0.0.2 500 external 192.168.10.2 500
diff --git a/extras/strongswan/vpp_sswan/Makefile b/extras/strongswan/vpp_sswan/Makefile
new file mode 100644
index 00000000000..254b90b3b09
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/Makefile
@@ -0,0 +1,116 @@
+# the directory to the strongSwan sources
+SWANDIR=${CURDIR}/../../../build-root/build-vpp-native/external/sswan
+# location of config.h
+CONFIGH=$(SWANDIR)/config.h
+# default install prefix: /usr/local or /usr
+PREFIX=/usr
+# location of the installed strongSwan libraries
+SWANLIBS=$(PREFIX)/lib/ipsec/
+# location of the strongSwan plugins
+SWANPLUGINS=$(PREFIX)/lib/ipsec/plugins
+# location of the strongSwan archive
+SWANARCHIVE=${CURDIR}/../../../build/external/downloads
+# default install configuration files:
+PREFIX_SYS_CONF=/etc
+# target location of the plugin config snippet: $(PREFIX)/etc/strongswan.d/charon/ or /etc/strongswan.d/charon/
+PLUGINCONF=$(PREFIX_SYS_CONF)/strongswan.d/charon/
+# location of the VPP libraries
+VPPLIBS=$(CURDIR)/../../../build-root/install-vpp-native/vpp/lib/x86_64-linux-gnu
+# the directory to the VPP sources
+VPPDIR=../../../build-root/install-vpp-native/vpp/include
+
+TARGET=libstrongswan-kernel-vpp.so
+
+# tested only with 5.9.5 and 5.9.6 version of strongSwan
+VERSION_SSWAN=5.9.6
+
+CFLAGS=-O2 -g -Wall -Wextra -fpic
+
+CFLAGS_I=-include $(CONFIGH) \
+ -I$(SWANDIR)/src/libstrongswan \
+ -I$(SWANDIR)/src/libcharon
+
+LDFLAGS= -lvppinfra \
+ -lvlibmemoryclient \
+ -lvlibapi \
+ -lsvm \
+ -lvppapiclient
+
+VERSION_VPP=$(shell (dpkg -s vpp | grep Version) | grep -Po '(?<=Version: )\d\d.\d\d')
+
+# check if VPP is installed
+ifneq ($(shell test "$(shell ldconfig -p | grep vppinfra.so | awk 'NR==1{print $$1;}')" && echo "yes"), yes)
+# check if VPPDIR exists
+ifeq ($(shell test -d $(VPPDIR) && echo "yes"), yes)
+ CFLAGS_I += -I$(VPPDIR)
+endif
+# check if VPPLIBS exists
+ifeq ($(shell test -d $(VPPLIBS) && echo "yes"), yes)
+ LDFLAGS += -L$(VPPLIBS)
+ LDFLAGS += -Wl,-rpath=$(VPPLIBS)
+endif
+endif
+
+SOURCES=$(wildcard *.c)
+OBJECTS=$(SOURCES:.c=.o)
+
+all: pull-swan install-swan $(TARGET)
+
+pull-swan:
+ @if [ -d "${SWANDIR}" ]; then \
+ rm -rf ${SWANDIR} ; \
+ fi
+ @if ! [ -f "${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz" ]; then \
+ curl -o ${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz -LO https://github.com/strongswan/strongswan/archive/${VERSION_SSWAN}.tar.gz; \
+ fi
+ @if ! [ -d "${CURDIR}/../../../build-root/build-vpp-native/external/" ]; then \
+ mkdir ${CURDIR}/../../../build-root/build-vpp-native/external; \
+ fi
+ tar -zxof ${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz -C ${CURDIR}/../../../build-root/build-vpp-native/external/
+ mv ${CURDIR}/../../../build-root/build-vpp-native/external/strongswan-${VERSION_SSWAN} ${SWANDIR}
+ cd ${SWANDIR} && ./autogen.sh
+ cd ${SWANDIR} && ./configure --prefix=${PREFIX} --sysconfdir=${PREFIX_SYS_CONF} --enable-libipsec --enable-systemd --enable-swanctl --disable-gmp --enable-openssl
+
+install-swan:
+ @if ! [ -f "${SWANDIR}/configure" ]; then \
+ 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
+
+# check if VPP is installed
+ifneq ($(shell test "$(shell ldconfig -p | grep vppinfra.so | awk 'NR==1{print $$1;}')" && echo "yes"), yes)
+ $(info INFO: Not found installed VPP - checking if locally VPP exists)
+# check if VPPDIR exists
+ifneq ($(shell test -d $(VPPDIR) && echo "yes"), yes)
+ $(error ERROR: Not found installed VPP and locally VPP - please install or build)
+else
+# check if VPPLIBS exists
+ifneq ($(shell test -d $(VPPLIBS) && echo "yes"), yes)
+ $(error ERROR: directory $(VPPLIBS) - doesn't exists, please compile VPP before build this)
+else
+ $(info INFO: Found locally VPP)
+endif
+endif
+else
+ $(info INFO: Found installed VPP in version: $(VERSION_VPP))
+endif
+
+$(TARGET): $(OBJECTS)
+ gcc $(CFLAGS) -shared -DPIC $(OBJECTS) $(LDFLAGS) -Wl,-soname -Wl,$(TARGET) -o $@
+ cp $(TARGET) ${SWANDIR}
+
+%.o: %.c
+ gcc $(CFLAGS) $(CFLAGS_I) -c $< -o $@ $(LDFLAGS)
+
+install:
+ cp $(TARGET) $(SWANPLUGINS)/$(TARGET)
+ cp kernel-vpp.conf $(PLUGINCONF)
+
+clean:
+ rm -f ${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz
+ rm -rf ${SWANDIR}
+ rm -f *.so *.o
+
+.PHONY: clean install all
diff --git a/extras/strongswan/vpp_sswan/README.rst b/extras/strongswan/vpp_sswan/README.rst
new file mode 100644
index 00000000000..57b30f452e5
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/README.rst
@@ -0,0 +1,170 @@
+.. _vpp_sswan_doc:
+
+VPP-SSWAN
+=======================
+
+``VPP-SSWAN`` is a StrongSwan plugin that helps offloading Strongswan IPsec ESP
+process from Linux Kernel to ``VPP``.
+
+The ``VPP-SSWAN`` takes advantage of ``StrongSwan`` extendable plugin design
+and translates ``StrongSwan`` SA creation/deletion and routing
+update operations into ``VPP`` C API calls. The successful execution of the
+API calls means the operations shall be performed by VPP smoothly.
+
+Inside ``VPP-SSWAN``, the kernel-vpp plugin is an interface to the IPsec and
+networking backend for `VPP <https://wiki.fd.io/view/VPP>`__ platform using
+the `VPP C API <https://wiki.fd.io/view/VPP/How_To_Use_The_C_API>`__.
+It provides address and routing lookup functionality and installs routes for
+IPsec traffic.
+
+The plugin also installs and maintains Security Associations and Policies to
+the `VPP IPsec <https://wiki.fd.io/view/VPP/IPSec_and_IKEv2#IPSec>`__.
+
+Since ``StrongSwan`` expects both IKE and IPsec traffic coming through the
+same network protected interfaces, the ``VPP-SSWAN`` expects the IKE traffic
+being diverted to Linux Kernel through the help of
+`VPP Linux Control Plane <https://s3-docs.fd.io/vpp/22.10/developer/plugins/
+lcp.html>`__. It is important to notice that due to LCP is a Tun/Tap interface,
+the IPsec performance will be limited by it if Transport mode of IPsec is used.
+
+Prerequisites
+-------------
+
+``VPP`` in release mode should be built before compiling ``vpp-swan plugin``.
+User may install ``StrongSwan`` prior to compile the plugin. However the
+plugin requires downloading ``StrongSwan`` source to include some of its
+header files to compile ``VPP-SSWAN``. In addition ``libsystemd-dev``
+should be installed prior to compile the plugin.
+
+Please Note: ONLY Strongswan version ``5.9.5`` and ``5.9.6`` were tested with
+this plugin.
+
+Build VPP Strongswan Plugin
+-------------
+
+``VPP-SSWAN`` requires ``StrongSwan`` source to compile. To obtain
+``StrongSwan`` the simplest way is to run the following commands:
+
+::
+
+ cd path/to/vpp/external/strongswan/vpp_swan/
+ make all
+
+Or you may download ``StrongSwan`` from its github page. It is recommended to
+use ``Strongswan`` version ``5.9.6`` or ``5.9.5`` for ``VPP-SSWAN`` to be
+compiled and integrate. The following steps are required for manually download
+``Strongswan`` source:
+
+- download strongswan source code to:
+``path/to/vpp/build/external/downloads``
+
+- unzip source code strongswan to:
+``path/to/vpp/build-root/build-vpp-native/external/sswan``
+
+- check if you have installed packages: ``libsystemd-dev`` on your OS
+
+- configure strongswan by:
+``./autogen.sh``
+``./configure --prefix=/usr --sysconfdir=/etc --enable-libipsec
+--enable-systemd --enable-swanctl --disable-gmp --enable-openssl``
+
+- compile ``vpp-swan plugin`` by:
+
+::
+
+ cd path/to/vpp/external/strongswan/vpp_swan/
+ make
+
+Build/install Strongswan (Optional)
+-------------
+
+In case you haven't installed ``Strongswan`` yet, you may use the following
+simple command to compile and install ``Strongswan`` from the downloaded source.
+
+::
+
+ cd path/to/vpp/external/strongswan/vpp_swan/
+ make pull-swan
+ make install-swan
+
+Install VPP-SWAN plugin into StrongSwan
+-------------
+
+After the ``VPP-SSWAN`` plugin has been built and ``Strongswan`` was installed,
+the following command will install the ``VPP-SSWAN`` plugin into ``Strongswan``.
+
+::
+
+ cd path/to/vpp/external/strongswan/vpp_swan/
+ make install
+
+Or you can manually copy ``libstrongswan-kernel-vpp.so`` into:
+``/usr/lib/ipsec/plugins``,
+and also ``kernel-vpp.conf`` into: ``/etc/strongswan.d/charon/``
+
+Now you can restart ``Strongswan`` by executing the following command:
+
+::
+
+ systemctl restart strongswan.service
+
+Configuration Strongswan
+-------------
+
+As an example, ``swanctl.conf`` file provides an example configuration to
+initialize connections between two endpoints.
+
+You may update the file based on your need and Copy into:
+``/etc/swanctl/conf.d/swanctl.conf``
+
+Configuration VPP
+-------------
+
+Some special treatment to VPP are required in your VPP ``startup.conf``.
+Since we use ``Strongswan`` to process IKE messages, we should disable VPP's
+IKEv2 plugin. Also as mentioned ``Linux Control Plane`` plugin is needed to
+route the traffic between VPP interface and Tun/Tap interface. To do so, simply
+adding the following commands:
+
+::
+
+ plugins {
+ plugin linux_cp_plugin.so { enable }
+ plugin ikev2_plugin.so { disable }
+ }
+
+ linux-cp {
+ lcp-sync
+ }
+
+Running VPP
+-------------
+
+Based on the provided sample ``swanctl.conf``, the following commands are
+required to be executed in ``VPP``:
+
+::
+
+ lcp create eth2 host-if eth2
+ set interface state eth2 up
+ set interface ip address eth2 192.168.0.2/24
+ set int state eth1 up
+ set int ip addr eth1 192.168.200.1/24
+
+In the commands above we assume ``eth2`` is the WAN interface to receive both
+IKE message and ESP encapsulated packets, and ``eth1`` is the LAN interface to
+receive plain packets to be encrypted. With the commands a ``Linux CP`` interface
+is created to mirror the ``eth2`` interface to Linux Kernel, and both interfaces
+were set the IP addresses followed by the ``swanctl.conf``.
+
+With the commands successfully executed and the security policy is succesfully
+agreed between two IKE daemons (one with VPP as IPsec processing engine), you may
+see the packets are encrypted/decrypted by VPP smoothly.
+
+Misc
+-------------
+This plugin is based on:
+`https://github.com/matfabia/strongswan
+<https://github.com/matfabia/strongswan>`__
+
+Author: Matus Fabian <matfabia@cisco.com>
diff --git a/extras/strongswan/vpp_sswan/docker/Dockerfile b/extras/strongswan/vpp_sswan/docker/Dockerfile
new file mode 100644
index 00000000000..616b72d3555
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/Dockerfile
@@ -0,0 +1,27 @@
+FROM jrei/systemd-ubuntu:20.04
+
+# add proxy according your own network
+#ENV http_proxy=""
+#ENV https_proxy=""
+#ENV no_proxy=""
+
+# update
+RUN apt-get update
+
+# tools
+RUN apt-get install -y git make wget libsystemd-dev
+RUN apt-get install -y sudo gperf bison flex
+RUN apt-get install -y iproute2 iputils-ping
+
+# setup env
+WORKDIR /root
+COPY ./docker/scripts/init_docker1.sh /root/
+COPY ./docker/scripts/init_docker2.sh /root/
+COPY ./docker/scripts/init.sh /root/
+COPY ./docker/scripts/run_vpp.sh /root/
+RUN chmod +x /root/init_docker1.sh
+RUN chmod +x /root/init_docker2.sh
+RUN chmod +x /root/init.sh
+RUN chmod +x /root/run_vpp.sh
+
+RUN ./init.sh
diff --git a/extras/strongswan/vpp_sswan/docker/configs/startup.conf b/extras/strongswan/vpp_sswan/docker/configs/startup.conf
new file mode 100644
index 00000000000..5c1952e1002
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/configs/startup.conf
@@ -0,0 +1,32 @@
+unix {
+ nodaemon
+ full-coredump
+ cli-listen /run/vpp/cli.sock
+ exec /root/vpp/extras/strongswan/vpp_sswan/docker/configs/vpp.conf
+}
+
+api-trace {
+ on
+}
+
+socksvr {
+ default
+}
+
+cpu {
+ main-core 1
+ corelist-workers 2
+}
+
+dpdk {
+ no-pci
+}
+
+plugins {
+ plugin linux_cp_plugin.so { enable }
+ plugin ikev2_plugin.so { disable }
+}
+
+linux-cp {
+ lcp-sync
+}
diff --git a/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker1.conf b/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker1.conf
new file mode 100644
index 00000000000..ac24bf5bb5d
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker1.conf
@@ -0,0 +1,35 @@
+connections {
+ net-net {
+ local_addrs = 192.168.0.2
+ remote_addrs = 192.168.0.1
+ local {
+ auth = psk
+ id = sun.strongswan.org
+ }
+ remote {
+ auth = psk
+ id = moon.strongswan.org
+ }
+ children {
+ net-net {
+ local_ts = 192.168.200.0/24
+ remote_ts = 192.168.100.0/24
+ esp_proposals = aes128-sha1-modp2048
+ rekey_time = 240m
+ }
+ }
+ version = 2
+ mobike = yes
+ encap = no # NAT-T if needed
+ proposals = aes128-sha256-x25519
+ }
+}
+secrets {
+ ike-net-net {
+ id = moon.strongswan.org
+ secret = simplepsk
+ }
+}
+
+# Include config snippets
+include conf.d/*.conf
diff --git a/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker2.conf b/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker2.conf
new file mode 100644
index 00000000000..a7ada86f499
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker2.conf
@@ -0,0 +1,35 @@
+connections {
+ net-net {
+ local_addrs = 192.168.0.1
+ remote_addrs = 192.168.0.2
+ local {
+ auth = psk
+ id = moon.strongswan.org
+ }
+ remote {
+ auth = psk
+ id = sun.strongswan.org
+ }
+ children {
+ net-net {
+ local_ts = 192.168.100.0/24
+ remote_ts = 192.168.200.0/24
+ esp_proposals = aes128-sha1-modp2048
+ rekey_time = 240m
+ }
+ }
+ version = 2
+ mobike = yes
+ encap = no # NAT-T if needed
+ proposals = aes128-sha256-x25519
+ }
+}
+secrets {
+ ike-net-net {
+ id = moon.strongswan.org
+ secret = simplepsk
+ }
+}
+
+# Include config snippets
+include conf.d/*.conf
diff --git a/extras/strongswan/vpp_sswan/docker/configs/vpp.conf b/extras/strongswan/vpp_sswan/docker/configs/vpp.conf
new file mode 100644
index 00000000000..dbf142d9ee4
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/configs/vpp.conf
@@ -0,0 +1,8 @@
+create host-interface name docker_1_eth2
+lcp create host-docker_1_eth2 host-if eth2
+set interface state host-docker_1_eth2 up
+set interface ip address host-docker_1_eth2 192.168.0.2/24
+
+create host-interface name docker_1a_eth1
+set interface state host-docker_1a_eth1 up
+set interface ip address host-docker_1a_eth1 192.168.200.1/24
diff --git a/extras/strongswan/vpp_sswan/docker/exposedockernetns.sh b/extras/strongswan/vpp_sswan/docker/exposedockernetns.sh
new file mode 100755
index 00000000000..ff223ce427a
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/exposedockernetns.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+if [ "$1" == "" ]; then
+ echo "usage: $0 <container_name>"
+ echo "Exposes the netns of a docker container to the host"
+ exit 1
+fi
+
+ pid=`docker inspect -f '{{.State.Pid}}' $1`
+ ln -s /proc/$pid/ns/net /var/run/netns/$1
+
+ echo "netns of ${1} exposed as /var/run/netns/${1}"
+
+ #echo "try: ip netns exec ${1} ip addr list"
diff --git a/extras/strongswan/vpp_sswan/docker/init_containers.sh b/extras/strongswan/vpp_sswan/docker/init_containers.sh
new file mode 100755
index 00000000000..c5a593d81af
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/init_containers.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+DOCKER_IMAGE_NAME="vppstrongswan"
+DOCKER_IMAGE_TAG="0.1"
+DOCKER_IMAGE_NAME_FULL="$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG"
+
+if [ "_$1" == "_build_docker_image" ];
+then
+ count=`docker image list | grep -c "$DOCKER_IMAGE_NAME.*$DOCKER_IMAGE_TAG"`
+ if [ $count -ne 0 ];
+ then
+ echo "Error: docker image $DOCKER_IMAGE_NAME_FULL already exists"
+ echo "Re-use it or remove to build new image"
+ exit 0
+ else
+ echo "### Building docker image $DOCKER_IMAGE_NAME ..."
+ cd ../ && docker build -t $DOCKER_IMAGE_NAME_FULL -f ./docker/Dockerfile .
+ echo "### Building docker image $DOCKER_IMAGE_NAME finished"
+ fi
+elif [ "_$1" == "_create_docker1" ];
+then
+ if [ "_$2" == "_" ];
+ then
+ exit 1
+ fi
+ DOCKER_CONTAINER_NAME="$2"
+
+ echo "### Creating container $DOCKER_CONTAINER_NAME"
+ docker run -itd --name="$DOCKER_CONTAINER_NAME" --privileged --cap-add=ALL -p 8022:22 -v /mnt/huge:/mnt/huge -v /sys/bus/pci/devices:/sys/bus/pci/devices -v /sys/devices/system/node:/sys/devices/system/node -v /lib/modules:/lib/modules -v /dev:/dev --tmpfs /tmp --tmpfs /run --tmpfs /run/lock --cgroupns=host -v /sys/fs/cgroup:/sys/fs/cgroup:rw "$DOCKER_IMAGE_NAME_FULL"
+ if [ $? -eq 0 ];
+ then
+ docker exec -i "$DOCKER_CONTAINER_NAME" "/root/init_docker1.sh" || { echo "call init_docker1.sh failed"; exit 127; }
+ fi
+ echo "### Creating container $DOCKER_CONTAINER_NAME finished"
+ exit 0
+elif [ "_$1" == "_create_docker2" ];
+then
+ if [ "_$2" == "_" ];
+ then
+ exit 1
+ fi
+ DOCKER_CONTAINER_NAME="$2"
+
+ echo "### Creating container $DOCKER_CONTAINER_NAME"
+ docker run -itd --name="$DOCKER_CONTAINER_NAME" --privileged --cap-add=ALL -p 8023:22 -v /mnt/huge:/mnt/huge -v /sys/bus/pci/devices:/sys/bus/pci/devices -v /sys/devices/system/node:/sys/devices/system/node -v /lib/modules:/lib/modules -v /dev:/dev --tmpfs /tmp --tmpfs /run --tmpfs /run/lock --cgroupns=host -v /sys/fs/cgroup:/sys/fs/cgroup:rw "$DOCKER_IMAGE_NAME_FULL"
+ if [ $? -eq 0 ];
+ then
+ docker exec -i "$DOCKER_CONTAINER_NAME" "/root/init_docker2.sh" || { echo "call init_docker2.sh failed"; exit 127; }
+ fi
+ echo "### Creating container $DOCKER_CONTAINER_NAME finished"
+ exit 0
+elif [ "_$1" == "_clean" ];
+then
+ if [ "_$2" == "_" ];
+ then
+ exit 1
+ fi
+ DOCKER_CONTAINER_NAME="$2"
+
+ echo "### Deleting container $DOCKER_CONTAINER_NAME"
+ sudo docker rm -f $DOCKER_CONTAINER_NAME
+ echo "### Deleting container $DOCKER_CONTAINER_NAME finished"
+ exit 0
+elif [ "_$1" == "_clean_image" ];
+then
+ echo "### Deleting image $DOCKER_IMAGE_NAME_FULL"
+ sudo docker rmi -f $DOCKER_IMAGE_NAME_FULL
+ echo "### Deleting image $DOCKER_IMAGE_NAME_FULL finished"
+ exit 0
+fi
diff --git a/extras/strongswan/vpp_sswan/docker/run.sh b/extras/strongswan/vpp_sswan/docker/run.sh
new file mode 100755
index 00000000000..3b1dc6d5223
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/run.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+DOCKER_1_NAME="vpp_sswan_docker1"
+DOCKER_2_NAME="vpp_sswan_docker2"
+
+if [ "_$1" == "_prepare_containers" ];
+then
+ echo "### Building docker image for vpp sswan plugin"
+ ./init_containers.sh build_docker_image
+ echo "### Building the first container for vpp sswan plugin"
+ ./init_containers.sh create_docker1 $DOCKER_1_NAME
+ echo "### Building the second container for vpp sswan plugin"
+ ./init_containers.sh create_docker2 $DOCKER_2_NAME
+elif [ "_$1" == "_config" ];
+then
+ echo "### Configuration $DOCKER_1_NAME and $DOCKER_2_NAME"
+ #ADD 1: set network namespace
+ echo "### Adding network namespace for $DOCKER_1_NAME and $DOCKER_2_NAME"
+ ip netns add vpp_sswan_temp
+ ./exposedockernetns.sh $DOCKER_1_NAME
+ ./exposedockernetns.sh $DOCKER_2_NAME
+ ip netns del vpp_sswan_temp
+ echo "### Adding network namespace for $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+ #ADD 2: settings network
+ echo "### Setting network for $DOCKER_1_NAME and $DOCKER_2_NAME"
+
+ ip link add docker_1_eth2 type veth peer name docker_2_eth2
+ ip link set netns $DOCKER_1_NAME dev docker_1_eth2
+ ip link set netns $DOCKER_2_NAME dev docker_2_eth2
+ #ADD 3: ip address
+ ip netns exec $DOCKER_2_NAME ip addr add 192.168.0.1/24 dev docker_2_eth2
+ ip netns exec $DOCKER_2_NAME ip link set dev docker_2_eth2 up
+
+ #LAN for Docker 1
+ ip link add docker_1a_eth1 type veth peer name docker_1b_eth1
+ ip link set netns $DOCKER_1_NAME dev docker_1a_eth1
+ ip link set netns $DOCKER_1_NAME dev docker_1b_eth1
+ ip netns exec $DOCKER_1_NAME ip addr add 192.168.200.10/24 dev docker_1b_eth1
+ ip netns exec $DOCKER_1_NAME ip link set dev docker_1b_eth1 up
+ ip netns exec $DOCKER_1_NAME ip route add 192.168.100.0/24 via 192.168.200.1 dev docker_1b_eth1
+
+ #LAN for Docker 2
+ ip link add docker_2a_eth1 type veth peer name docker_2b_eth1
+ ip link set netns $DOCKER_2_NAME dev docker_2a_eth1
+ ip link set netns $DOCKER_2_NAME dev docker_2b_eth1
+ ip netns exec $DOCKER_2_NAME ip addr add 192.168.100.1/24 dev docker_2a_eth1
+ ip netns exec $DOCKER_2_NAME ip addr add 192.168.100.10/24 dev docker_2b_eth1
+ ip netns exec $DOCKER_2_NAME ip link set dev docker_2a_eth1 up
+ ip netns exec $DOCKER_2_NAME ip link set dev docker_2b_eth1 up
+ ip netns exec $DOCKER_2_NAME ip route add 192.168.200.0/24 via 192.168.100.1 dev docker_2b_eth1
+
+ echo "### Setting network for $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+ #ADD 4: run VPP on the first docker
+ echo "### Running VPP and sswan on: $DOCKER_1_NAME and $DOCKER_2_NAME"
+ docker exec -i "$DOCKER_1_NAME" "/root/run_vpp.sh"
+ docker exec -d $DOCKER_2_NAME systemctl restart strongswan.service
+ echo "### Running VPP and sswan on: $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+ #ADD 5: initiate sswan
+ echo "### initiate SSWAN between $DOCKER_1_NAME and $DOCKER_2_NAME"
+ docker exec -i $DOCKER_1_NAME swanctl --initiate --child net-net
+ echo "### initiate SSWAN between $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+elif [ "_$1" == "_clean" ];
+then
+ #DELETE 5: initiate sswan
+ echo "### Terminate SSWAN between $DOCKER_1_NAME and $DOCKER_2_NAME"
+ docker exec -i $DOCKER_1_NAME swanctl --terminate --child net-net
+ echo "### Terminate SSWAN between $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+ #DELETE 4: run VPP on the first docker
+ echo "### Exit VPP on: $DOCKER_1_NAME"
+ docker exec -d $DOCKER_1_NAME pkill -9 -f vpp
+ echo "### Exit VPP on: $DOCKER_1_NAME finished"
+
+ echo "### Deletting settings network for $DOCKER_1_NAME and $DOCKER_2_NAME"
+ #DELETE 3: ip address
+ ip netns exec $DOCKER_1_NAME ip link set dev docker_1_eth2 down
+ ip netns exec $DOCKER_2_NAME ip link set dev docker_2_eth2 down
+ #docker 1
+ ip netns exec $DOCKER_1_NAME ip link set dev docker_1b_eth1 down
+ ip netns exec $DOCKER_1_NAME ip link set netns 1 dev docker_1a_eth1
+ ip netns exec $DOCKER_1_NAME ip link set netns 1 dev docker_1b_eth1
+ ip link del docker_1a_eth1 type veth peer name docker_1b_eth1
+
+ #docker 2
+ ip netns exec $DOCKER_2_NAME ip link set dev docker_2a_eth1 down
+ ip netns exec $DOCKER_2_NAME ip link set dev docker_2b_eth1 down
+ ip netns exec $DOCKER_2_NAME ip link set netns 1 dev docker_2a_eth1
+ ip netns exec $DOCKER_2_NAME ip link set netns 1 dev docker_2b_eth1
+ ip link del docker_2a_eth1 type veth peer name docker_2b_eth1
+
+ #DELETE 2: settings network
+ ip netns exec $DOCKER_1_NAME ip link set netns 1 dev docker_1_eth2
+ ip netns exec $DOCKER_2_NAME ip link set netns 1 dev docker_2_eth2
+ ip link del docker_1_eth2 type veth peer name docker_2_eth2
+ echo "### Deletting settings network for $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+ #DELETE 1: delete network namespace
+ echo "### Deleting network namespace for $DOCKER_1_NAME and $DOCKER_2_NAME"
+ ip netns del $DOCKER_1_NAME
+ ip netns del $DOCKER_2_NAME
+ echo "### Deleting network namespace for $DOCKER_1_NAME and $DOCKER_2_NAME finished"
+
+elif [ "_$1" == "_deleted" ];
+then
+ echo "### Exit VPP on: $DOCKER_1_NAME"
+ docker exec -d $DOCKER_1_NAME pkill -9 -f vpp
+ echo "### Exit VPP on: $DOCKER_1_NAME finished"
+
+ echo "### Deleting container $DOCKER_1_NAME and $DOCKER_2_NAME"
+ ./init_containers.sh clean $DOCKER_1_NAME
+ ./init_containers.sh clean $DOCKER_2_NAME
+ echo "### Deleting image"
+ ./init_containers.sh clean_image
+fi
diff --git a/extras/strongswan/vpp_sswan/docker/scripts/init.sh b/extras/strongswan/vpp_sswan/docker/scripts/init.sh
new file mode 100644
index 00000000000..97a087b1a2f
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/scripts/init.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+echo "Initialization Image"
+
+git clone https://github.com/FDio/vpp.git ./vpp
+
+cd vpp
+yes | make install-dep
diff --git a/extras/strongswan/vpp_sswan/docker/scripts/init_docker1.sh b/extras/strongswan/vpp_sswan/docker/scripts/init_docker1.sh
new file mode 100644
index 00000000000..51e7b3d7851
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/scripts/init_docker1.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+echo "Initialization Docker 1 - VPP with SSWAN"
+
+cd /root/vpp
+make build-release
+
+cd /root/vpp/extras/strongswan/vpp_sswan
+make clean
+make all
+
+cd /root/vpp/build-root/build-vpp-native/external/sswan
+sudo make install
+
+cd /root/vpp/extras/strongswan/vpp_sswan
+make install
+
+sudo systemctl daemon-reload
+sudo systemctl restart strongswan.service
+
+echo "### Loaded plugin in strogswan"
+sudo swanctl --stats
+
+sudo cp /root/vpp/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker1.conf /etc/swanctl/conf.d/swanctl.conf
diff --git a/extras/strongswan/vpp_sswan/docker/scripts/init_docker2.sh b/extras/strongswan/vpp_sswan/docker/scripts/init_docker2.sh
new file mode 100644
index 00000000000..237a341a1f8
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/scripts/init_docker2.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+echo "Initialization Docker 2 - SSWAN in kernel"
+
+# tested only with 5.9.5 and 5.9.6 version of strongSwan
+VERSION_SSWAN=5.9.6
+
+curl -o ./strongswan-${VERSION_SSWAN}.tar.gz -LO https://github.com/strongswan/strongswan/archive/${VERSION_SSWAN}.tar.gz;
+tar -zxof ./strongswan-${VERSION_SSWAN}.tar.gz
+
+cd /root/strongswan-${VERSION_SSWAN}
+./autogen.sh
+./configure --prefix=/usr --sysconfdir=/etc --enable-libipsec --enable-systemd --enable-swanctl --disable-gmp --enable-openssl
+make -j$(nproc)
+sudo make install
+
+sudo cp /root/vpp/extras/strongswan/vpp_sswan/docker/configs/swanctl_docker2.conf /etc/swanctl/conf.d/swanctl.conf
+
+sudo systemctl daemon-reload
+sudo systemctl restart strongswan.service
+
+echo "### Loaded plugin in strogswan"
+sudo swanctl --stats
diff --git a/extras/strongswan/vpp_sswan/docker/scripts/run_vpp.sh b/extras/strongswan/vpp_sswan/docker/scripts/run_vpp.sh
new file mode 100644
index 00000000000..e21d92503eb
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/docker/scripts/run_vpp.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+cd /root/vpp/
+make run-release STARTUP_CONF=/root/vpp/extras/strongswan/vpp_sswan/docker/configs/startup.conf &
+
+sleep 5
+
+sudo systemctl restart strongswan.service
+
+sleep 2
+
+echo "### Checking connections between VPP and Strongswan"
+/root/vpp/build-root/build-vpp-native/vpp/bin/vppctl -s /run/vpp/cli.sock sh api client
diff --git a/extras/strongswan/vpp_sswan/kernel-vpp.conf b/extras/strongswan/vpp_sswan/kernel-vpp.conf
new file mode 100644
index 00000000000..71f50b5bf08
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel-vpp.conf
@@ -0,0 +1,7 @@
+kernel-vpp {
+
+ # Whether to load the plugin. Can also be an integer to increase the
+ # priority of this plugin.
+ load = yes
+
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c b/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c
new file mode 100644
index 00000000000..e1c71edc836
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c
@@ -0,0 +1,1997 @@
+/*
+ * Copyright (c) 2022 Intel 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 <daemon.h>
+#include <utils/debug.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/vnet.h>
+#include <collections/hashtable.h>
+#include <threading/mutex.h>
+#include <processing/jobs/callback_job.h>
+#include <vpp-api/client/stat_client.h>
+
+#define vl_typedefs
+#define vl_endianfun
+/* Include the (first) vlib-api API definition layer */
+#include <vlibmemory/vl_memory_api_h.h>
+/* Include the current layer (third) vpp API definition layer */
+#include <vpp/api/vpe_types.api.h>
+#include <vpp/api/vpe.api.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
+#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
+#include <vnet/ipsec/ipsec.api_enum.h>
+#include <vnet/ipsec/ipsec.api_types.h>
+#include <vnet/interface.api_enum.h>
+#include <vnet/interface.api_types.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+#include "kernel_vpp_ipsec.h"
+#include "kernel_vpp_shared.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <net/if_arp.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#define PRIO_BASE 384
+
+u32 natt_port;
+
+/**
+ * One and only instance of the daemon.
+ */
+daemon_t *charon;
+
+typedef struct private_kernel_vpp_ipsec_t private_kernel_vpp_ipsec_t;
+
+/**
+ * Private variables of kernel_vpp_ipsec class.
+ */
+struct private_kernel_vpp_ipsec_t
+{
+
+ /**
+ * Public interface
+ */
+ kernel_vpp_ipsec_t public;
+
+ /**
+ * Next security association database entry ID to allocate
+ */
+ refcount_t next_sad_id;
+
+ /**
+ * Next security policy database entry ID to allocate
+ */
+ refcount_t next_spd_id;
+
+ /**
+ * Mutex to lock access to installed policies
+ */
+ mutex_t *mutex;
+
+ /**
+ * Hash table of instaled SA, as kernel_ipsec_sa_id_t => sa_t
+ */
+ hashtable_t *sas;
+
+ /**
+ * Hash table of security policy databases, as nterface => spd_t
+ */
+ hashtable_t *spds;
+
+ /**
+ * Linked list of installed routes
+ */
+ linked_list_t *routes;
+
+ /**
+ * Next SPI to allocate
+ */
+ refcount_t nextspi;
+
+ /**
+ * Mix value to distribute SPI allocation randomly
+ */
+ uint32_t mixspi;
+
+ /**
+ * Whether to install routes along policies
+ */
+ bool install_routes;
+
+ /**
+ * Whether to install SAs with tunnel flag. Disabling this can be useful
+ * in some scenarios e.g. using SAs to "ipsec tunnel protect" for the
+ * route-based IPsec
+ */
+ bool use_tunnel_mode_sa;
+
+ /**
+ * Connections to VPP Stats
+ */
+ stat_client_main_t *sm;
+};
+
+/**
+ * Security association entry
+ */
+typedef struct
+{
+ /** VPP SA ID */
+ uint32_t sa_id;
+ uint32_t stat_index;
+ kernel_ipsec_sa_id_t *sa_id_p;
+} sa_t;
+
+/**
+ * Security policy database
+ */
+typedef struct
+{
+ /** VPP SPD ID */
+ uint32_t spd_id;
+ /** Networking interface ID restricting policy */
+ uint32_t sw_if_index;
+ /** Policy count for this SPD */
+ refcount_t policy_num;
+ /** Name of the interface the SPD is bound to */
+ char *if_name;
+} spd_t;
+
+/**
+ * Installed route
+ */
+typedef struct
+{
+ /** Name of the interface the route is bound to */
+ char *if_name;
+ /** Gateway of route */
+ host_t *gateway;
+ /** Destination network of route */
+ host_t *dst_net;
+ /** Prefix length of dst_net */
+ uint8_t prefixlen;
+ /** References for route */
+ refcount_t refs;
+} route_entry_t;
+
+#define htonll(x) \
+ ((1 == htonl (1)) ? \
+ (x) : \
+ ((uint64_t) htonl ((x) &0xFFFFFFFF) << 32) | htonl ((x) >> 32))
+#define ntohll(x) \
+ ((1 == ntohl (1)) ? \
+ (x) : \
+ ((uint64_t) ntohl ((x) &0xFFFFFFFF) << 32) | ntohl ((x) >> 32))
+
+CALLBACK (route_equals, bool, route_entry_t *a, va_list args)
+{
+ host_t *dst_net, *gateway;
+ uint8_t *prefixlen;
+ char *if_name;
+
+ VA_ARGS_VGET (args, if_name, gateway, dst_net, prefixlen);
+
+ return a->if_name && if_name && streq (a->if_name, if_name) &&
+ a->gateway->ip_equals (a->gateway, gateway) &&
+ a->dst_net->ip_equals (a->dst_net, dst_net) &&
+ a->prefixlen == *prefixlen;
+}
+
+/**
+ * Clean up a route entry
+ */
+static void
+route_destroy (route_entry_t *this)
+{
+ this->dst_net->destroy (this->dst_net);
+ this->gateway->destroy (this->gateway);
+ free (this->if_name);
+ free (this);
+}
+
+static uint32_t get_sw_if_index ();
+
+static int
+set_arp (char *ipStr, char *if_name, bool add)
+{
+ char *out = NULL;
+ int out_len = 0;
+ vl_api_ip_neighbor_add_del_t *mp = NULL;
+ vl_api_ip_neighbor_add_del_reply_t *rmp = NULL;
+ int rc = SUCCESS;
+ uint32_t sw_if_index = ~0;
+
+ FILE *fp;
+ int nread = 0;
+ ssize_t len = 0;
+ char *buffer = NULL;
+ char buf[2][20];
+ char *file = "/proc/net/arp";
+ unsigned char mac[8] = {
+ 0,
+ };
+ uint32_t addr = 0;
+
+ if (if_name == NULL || ipStr == NULL)
+ {
+ DBG2 (DBG_KNL, "para is null\n");
+ rc = FAILED;
+ goto error;
+ }
+ DBG2 (DBG_KNL, "from kernel read mac\n");
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ sw_if_index = get_sw_if_index (if_name);
+ if (sw_if_index == ~0)
+ {
+ DBG1 (DBG_KNL, "sw_if_index for %s not found", if_name);
+ goto error;
+ }
+
+ fp = fopen (file, "rb");
+ while (fp && ((nread = getline (&buffer, &len, fp)) != -1))
+ {
+ sscanf (buffer, "%s %*s %*s %s %*s %*s", &buf[0], &buf[1]);
+ inet_aton (&buf[0], &addr);
+
+ if (addr == *((u32 *) (ipStr)))
+ {
+ sscanf (buf[1], "%02x:%02x:%02x:%02x:%02x:%02x", &mac[0], &mac[1],
+ &mac[2], &mac[3], &mac[4], &mac[5]);
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ip_neighbor_add_del_0607c257");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->is_add = add;
+ memcpy (mp->neighbor.ip_address.un.ip4, (u8 *) &addr, sizeof (addr));
+ mp->neighbor.ip_address.af = 0;
+ memcpy (mp->neighbor.mac_address, mac, 6);
+ mp->neighbor.sw_if_index = htonl (sw_if_index);
+ mp->neighbor.flags = 1;
+
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s neighbor entry",
+ add ? "adding" : "removing");
+ fclose (fp);
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s neighbor add rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ fclose (fp);
+ goto error;
+ }
+ fclose (fp);
+ free (out);
+ vl_msg_api_free (mp);
+ free (buffer);
+
+ return rc;
+ }
+ }
+
+ if (fp != NULL)
+ {
+ fclose (fp);
+ fp = NULL;
+ }
+
+error:
+ free (out);
+ vl_msg_api_free (mp);
+ if (buffer != NULL)
+ {
+ free (buffer);
+ buffer = NULL;
+ }
+ return rc;
+}
+
+static int
+add_Route (char *ipAddr, int len, char *mask, char *gateWay)
+{
+ int fd;
+ int rc = SUCCESS;
+ struct sockaddr_in _sin;
+ struct sockaddr_in *sin = &_sin;
+ struct rtentry rt;
+
+ do
+ {
+ fd = socket (AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ {
+ DBG2 (DBG_KNL, "addRoute: socket error\n");
+ rc = FAILED;
+ break;
+ }
+ memset (&rt, 0, sizeof (struct rtentry));
+ memset (sin, 0, sizeof (struct sockaddr_in));
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+
+ if (inet_aton (gateWay, &sin->sin_addr) < 0)
+ {
+ rc = FAILED;
+ break;
+ }
+ memcpy (&rt.rt_gateway, sin, sizeof (struct sockaddr_in));
+
+ ((struct sockaddr_in *) &rt.rt_dst)->sin_family = AF_INET;
+ memcpy (&((struct sockaddr_in *) &rt.rt_dst)->sin_addr, ipAddr, len);
+
+ ((struct sockaddr_in *) &rt.rt_genmask)->sin_family = AF_INET;
+ if (inet_aton (mask,
+ &((struct sockaddr_in *) &rt.rt_genmask)->sin_addr) < 0)
+ {
+ rc = FAILED;
+ break;
+ }
+ rt.rt_flags = RTF_GATEWAY;
+ if (ioctl (fd, SIOCADDRT, &rt) < 0)
+ {
+ rc = FAILED;
+ }
+ }
+ while (0);
+
+ close (fd);
+ return rc;
+}
+
+static int
+set_address (u32 ipAddr, u32 sw_if_index, bool add)
+{
+ char *out = NULL;
+ int out_len = 0;
+ vl_api_sw_interface_add_del_address_t *mp;
+ vl_api_sw_interface_add_del_address_reply_t *rmp;
+
+ int rc = SUCCESS;
+
+ uint32_t addr;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "sw_interface_add_del_address_5463d73b");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->is_add = add;
+ memcpy (mp->prefix.address.un.ip4, (u8 *) &ipAddr, sizeof (ipAddr));
+ mp->prefix.len = 24;
+ mp->sw_if_index = sw_if_index;
+
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG2 (DBG_KNL, "vac %s neighbor entry", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG2 (DBG_KNL, "%s neighbor add rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ return rc;
+
+error:
+ free (out);
+ vl_msg_api_free (mp);
+ return rc;
+}
+
+/**
+ * (Un)-install a single route
+ */
+static void
+manage_route (private_kernel_vpp_ipsec_t *this, bool add,
+ traffic_selector_t *dst_ts, host_t *src, host_t *dst)
+{
+ host_t *dst_net = NULL, *gateway = NULL;
+ uint8_t prefixlen;
+ char *if_name = NULL;
+ route_entry_t *route;
+ bool route_exist = FALSE;
+
+ char *netmask = "255.255.255.0";
+ char *tap_gateway = "1.1.1.1";
+ int arp_rc = 0;
+ if (dst->is_anyaddr (dst))
+ {
+ return;
+ }
+ gateway =
+ charon->kernel->get_nexthop (charon->kernel, dst, -1, NULL, &if_name);
+ dst_ts->to_subnet (dst_ts, &dst_net, &prefixlen);
+ if (!if_name)
+ {
+ if (src->is_anyaddr (src))
+ {
+ goto error;
+ }
+ if (!charon->kernel->get_interface (charon->kernel, src, &if_name))
+ {
+ goto error;
+ }
+ }
+ route_exist =
+ this->routes->find_first (this->routes, route_equals, (void **) &route,
+ if_name, gateway, dst_net, &prefixlen);
+ if (add)
+ {
+ DBG2 (DBG_KNL, "installing route: %H/%d via %H dev %s", dst_net,
+ prefixlen, gateway, if_name);
+ if (route_exist)
+ {
+ unsigned int refs_num = ref_get (&route->refs);
+ DBG2 (DBG_KNL, "add route but it exist %d", refs_num);
+ }
+ else
+ {
+ INIT (route, .if_name = strdup (if_name),
+ .gateway = gateway->clone (gateway),
+ .dst_net = dst_net->clone (dst_net), .prefixlen = prefixlen,
+ .refs = 1, );
+ this->routes->insert_last (this->routes, route);
+ charon->kernel->add_route (charon->kernel,
+ dst_net->get_address (dst_net), prefixlen,
+ gateway, dst, if_name, 1);
+ }
+
+ add_Route (dst_net->get_address (dst_net).ptr,
+ dst_net->get_address (dst_net).len, netmask, tap_gateway);
+
+ arp_rc = set_arp (gateway->get_address (gateway).ptr, if_name, TRUE);
+ if (arp_rc)
+ DBG2 (DBG_KNL, "arpGet success!\n");
+ }
+ else
+ {
+ DBG2 (DBG_KNL, "uninstalling route: %H/%d via %H dev %s", dst_net,
+ prefixlen, gateway, if_name);
+ if (!route_exist)
+ {
+ DBG2 (DBG_KNL, "del route but it not exist");
+ goto error;
+ }
+ if (ref_put (&route->refs))
+ {
+ this->routes->remove (this->routes, route, NULL);
+ route_destroy (route);
+ charon->kernel->del_route (charon->kernel,
+ dst_net->get_address (dst_net), prefixlen,
+ gateway, dst, if_name, 1);
+ }
+ }
+error:
+ if (gateway != NULL)
+ gateway->destroy (gateway);
+ if (dst_net != NULL)
+ dst_net->destroy (dst_net);
+ if (if_name != NULL)
+ free (if_name);
+ return;
+}
+
+/**
+ * Hash function for IPsec SA
+ */
+static u_int
+sa_hash (kernel_ipsec_sa_id_t *sa)
+{
+ return chunk_hash_inc (
+ sa->src->get_address (sa->src),
+ chunk_hash_inc (
+ sa->dst->get_address (sa->dst),
+ chunk_hash_inc (chunk_from_thing (sa->spi),
+ chunk_hash (chunk_from_thing (sa->proto)))));
+}
+
+/**
+ * Equality function for IPsec SA
+ */
+static bool
+sa_equals (kernel_ipsec_sa_id_t *sa, kernel_ipsec_sa_id_t *other_sa)
+{
+ return sa->src->ip_equals (sa->src, other_sa->src) &&
+ sa->dst->ip_equals (sa->dst, other_sa->dst) &&
+ sa->spi == other_sa->spi && sa->proto == other_sa->proto;
+}
+
+/**
+ * Equality function for policy SPD
+ */
+static bool
+policy_equals (vl_api_ipsec_spd_entry_t *policy,
+ vl_api_ipsec_spd_entry_t *other_policy)
+{
+
+ /* change protocol due to legacy implementation of ANY protocol inside VPP */
+ if (other_policy->protocol == 255)
+ other_policy->protocol = 0;
+
+ /* return true if both policies are equal */
+ return !memcmp (policy, other_policy, sizeof (*policy));
+}
+
+/**
+ * Hash function for interface
+ */
+static u_int
+interface_hash (char *interface)
+{
+ return chunk_hash (chunk_from_str (interface));
+}
+
+/**
+ * Equality function for interface
+ */
+static bool
+interface_equals (char *interface1, char *interface2)
+{
+ return streq (interface1, interface2);
+}
+
+/**
+ * Map an integer x with a one-to-one function using quadratic residues
+ */
+static u_int
+permute (u_int x, u_int p)
+{
+ u_int qr;
+
+ x = x % p;
+ qr = ((uint64_t) x * x) % p;
+ if (x <= p / 2)
+ {
+ return qr;
+ }
+ return p - qr;
+}
+
+/**
+ * Initialize seeds for SPI generation
+ */
+static bool
+init_spi (private_kernel_vpp_ipsec_t *this)
+{
+ bool ok = TRUE;
+ rng_t *rng;
+
+ rng = lib->crypto->create_rng (lib->crypto, RNG_STRONG);
+ if (!rng)
+ {
+ return FALSE;
+ }
+ ok =
+ rng->get_bytes (rng, sizeof (this->nextspi), (uint8_t *) &this->nextspi);
+ if (ok)
+ {
+ ok =
+ rng->get_bytes (rng, sizeof (this->mixspi), (uint8_t *) &this->mixspi);
+ }
+ rng->destroy (rng);
+ return ok;
+}
+
+/**
+ * Calculate policy priority
+ */
+static uint32_t
+calculate_priority (policy_priority_t policy_priority, traffic_selector_t *src,
+ traffic_selector_t *dst)
+{
+ uint32_t priority = PRIO_BASE;
+ uint16_t port;
+ uint8_t mask, proto;
+ host_t *net;
+
+ switch (policy_priority)
+ {
+ case POLICY_PRIORITY_FALLBACK:
+ priority <<= 1;
+ /* fall-through */
+ case POLICY_PRIORITY_ROUTED:
+ priority <<= 1;
+ /* fall-through */
+ case POLICY_PRIORITY_DEFAULT:
+ priority <<= 1;
+ /* fall-through */
+ case POLICY_PRIORITY_PASS:
+ break;
+ }
+ /* calculate priority based on selector size, small size = high prio */
+ src->to_subnet (src, &net, &mask);
+ priority -= mask;
+ proto = src->get_protocol (src);
+ port = net->get_port (net);
+ net->destroy (net);
+
+ dst->to_subnet (dst, &net, &mask);
+ priority -= mask;
+ proto = max (proto, dst->get_protocol (dst));
+ port = max (port, net->get_port (net));
+ net->destroy (net);
+
+ priority <<= 2; /* make some room for the two flags */
+ priority += port ? 0 : 2;
+ priority += proto ? 0 : 1;
+ return priority;
+}
+
+/**
+ * Get sw_if_index from interface name
+ */
+static uint32_t
+get_sw_if_index (char *interface)
+{
+ char *out = NULL;
+ int out_len, name_filter_len = 0, msg_len = 0;
+ int num, i;
+ vl_api_sw_interface_dump_t *mp = NULL;
+ vl_api_sw_interface_details_t *rmp = NULL;
+ uint32_t sw_if_index = ~0;
+
+ if (interface == NULL)
+ goto error;
+
+ name_filter_len = strlen (interface);
+ msg_len = sizeof (*mp) + name_filter_len;
+ mp = vl_msg_api_alloc (msg_len);
+ clib_memset (mp, 0, msg_len);
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "sw_interface_dump_aa610c27");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->name_filter_valid = TRUE;
+ mp->name_filter.length = htonl (name_filter_len);
+ memcpy ((char *) mp->name_filter.buf, interface, name_filter_len);
+
+ if (vac->send_dump (vac, (char *) mp, msg_len, &out, &out_len))
+ {
+ goto error;
+ }
+ if (!out_len)
+ {
+ goto error;
+ }
+ num = out_len / sizeof (*rmp);
+ rmp = (vl_api_sw_interface_details_t *) out;
+ for (i = 0; i < num; i++)
+ {
+ if (strlen (rmp->interface_name) &&
+ streq (interface, rmp->interface_name))
+ {
+ sw_if_index = ntohl (rmp->sw_if_index);
+ break;
+ }
+ rmp += 1;
+ }
+
+error:
+ if (out)
+ free (out);
+ if (mp)
+ vl_msg_api_free (mp);
+ return sw_if_index;
+}
+
+/**
+ * (Un)-install a security policy database
+ */
+static status_t
+spd_add_del (bool add, uint32_t spd_id)
+{
+ char *out = NULL;
+ int out_len;
+ vl_api_ipsec_spd_add_del_t *mp;
+ vl_api_ipsec_spd_add_del_reply_t *rmp;
+ status_t rv = FAILED;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_add_del_20e89a95");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->is_add = add;
+ mp->spd_id = htonl (spd_id);
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ rv = SUCCESS;
+
+error:
+ free (out);
+ vl_msg_api_free (mp);
+ return rv;
+}
+
+/**
+ * Enable or disable SPD on an insterface
+ */
+static status_t
+interface_add_del_spd (bool add, uint32_t spd_id, uint32_t sw_if_index)
+{
+ char *out = NULL;
+ int out_len;
+ vl_api_ipsec_interface_add_del_spd_t *mp;
+ vl_api_ipsec_interface_add_del_spd_reply_t *rmp;
+ status_t rv = FAILED;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_interface_add_del_spd_80f80cbb");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->is_add = add;
+ mp->spd_id = htonl (spd_id);
+ mp->sw_if_index = htonl (sw_if_index);
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s interface SPD failed",
+ add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s interface SPD failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ rv = SUCCESS;
+
+error:
+ free (out);
+ vl_msg_api_free (mp);
+ return rv;
+}
+
+static int
+bypass_all (bool add, uint32_t spd_id, uint32_t sa_id, uint32_t priority)
+{
+ vl_api_ipsec_spd_entry_add_del_t *mp;
+ vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
+ char *out = NULL;
+ int out_len;
+ status_t rv = FAILED;
+
+ DBG2 (DBG_KNL, "bypass_all [%s] spd_id %d sa_id %d", add ? "ADD" : "DEL",
+ spd_id, sa_id);
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_entry_add_del_338b7411");
+ mp->_vl_msg_id = ntohs (msg_id);
+ mp->is_add = add;
+ mp->entry.sa_id = ntohl (sa_id);
+ mp->entry.spd_id = ntohl (spd_id);
+ mp->entry.priority = ntohl (priority - POLICY_PRIORITY_PASS);
+ mp->entry.is_outbound = 0;
+ mp->entry.policy = ntohl (IPSEC_API_SPD_ACTION_BYPASS);
+ memset (mp->entry.local_address_stop.un.ip6, 0xFF, 16);
+ memset (mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
+ mp->entry.remote_port_start = mp->entry.local_port_start = ntohs (0);
+ mp->entry.remote_port_stop = mp->entry.local_port_stop = ntohs (0xFFFF);
+ mp->entry.protocol = IP_API_PROTO_ESP;
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ /* address "out" needs to be freed after vec->send */
+ if (out != NULL)
+ {
+ free (out);
+ out = NULL;
+ }
+ mp->entry.is_outbound = 1;
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ /* address "out" needs to be freed after vec->send */
+ if (out != NULL)
+ {
+ free (out);
+ out = NULL;
+ }
+ mp->entry.is_outbound = 0;
+ mp->entry.protocol = IP_API_PROTO_AH;
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ /* address "out" needs to be freed after vec->send */
+ if (out != NULL)
+ {
+ free (out);
+ out = NULL;
+ }
+ mp->entry.is_outbound = 1;
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+
+ rv = SUCCESS;
+
+error:
+ if (out)
+ free (out);
+ vl_msg_api_free (mp);
+
+ return rv;
+}
+
+static int
+bypass_port (bool add, uint32_t spd_id, uint32_t sa_id, uint16_t port,
+ uint32_t priority)
+{
+ vl_api_ipsec_spd_entry_add_del_t *mp;
+ vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
+ char *out = NULL;
+ int out_len;
+ status_t rv = FAILED;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_entry_add_del_338b7411");
+ mp->_vl_msg_id = ntohs (msg_id);
+ mp->is_add = add;
+ mp->entry.sa_id = ntohl (sa_id);
+ mp->entry.spd_id = ntohl (spd_id);
+ mp->entry.priority = ntohl (priority - POLICY_PRIORITY_PASS);
+ mp->entry.policy = ntohl (IPSEC_API_SPD_ACTION_BYPASS);
+ memset (mp->entry.local_address_stop.un.ip6, 0xFF, 16);
+ memset (mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
+ mp->entry.is_outbound = 0;
+ mp->entry.remote_port_start = mp->entry.local_port_start = ntohs (port);
+ mp->entry.remote_port_stop = mp->entry.local_port_stop = ntohs (port);
+ mp->entry.protocol = IP_API_PROTO_UDP;
+
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ /* address "out" needs to be freed after vec->send */
+ if (out != NULL)
+ {
+ free (out);
+ out = NULL;
+ }
+ mp->entry.is_outbound = 1;
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+ rv = SUCCESS;
+
+error:
+ if (out)
+ free (out);
+ vl_msg_api_free (mp);
+
+ return rv;
+}
+
+/**
+ * Add or remove a bypass policy
+ */
+static status_t
+manage_bypass (bool add, uint32_t spd_id, uint32_t sa_id, uint32_t priority)
+{
+ uint16_t port;
+ status_t rv;
+
+ bypass_all (add, spd_id, sa_id, priority);
+
+ port =
+ lib->settings->get_int (lib->settings, "%s.port", IKEV2_UDP_PORT, lib->ns);
+
+ if (port)
+ {
+ rv = bypass_port (add, spd_id, sa_id, port, priority);
+ if (rv != SUCCESS)
+ {
+ return rv;
+ }
+ }
+
+ port = lib->settings->get_int (lib->settings, "%s.port_nat_t",
+ IKEV2_NATT_PORT, lib->ns);
+ if (port)
+ {
+ rv = bypass_port (add, spd_id, sa_id, port, priority);
+ if (rv != SUCCESS)
+ {
+ return rv;
+ }
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Add or remove a policy
+ */
+static status_t
+manage_policy (private_kernel_vpp_ipsec_t *this, bool add,
+ kernel_ipsec_policy_id_t *id,
+ kernel_ipsec_manage_policy_t *data)
+{
+ spd_t *spd = NULL;
+ char *out = NULL, *interface = NULL;
+ int out_len;
+ uint32_t sw_if_index, spd_id = ~0, sad_id = ~0;
+ status_t rv = FAILED;
+ uint32_t priority, auto_priority;
+ chunk_t src_from, src_to, dst_from, dst_to;
+ host_t *src = NULL, *dst = NULL, *addr = NULL;
+ vl_api_ipsec_spd_entry_add_del_t *mp = NULL;
+ vl_api_ipsec_spd_entry_add_del_reply_t *rmp = NULL;
+ bool n_spd = false; /* is a new SPD? */
+ vl_api_ipsec_spd_dump_t *mp_dump = NULL;
+ vl_api_ipsec_spd_details_t *rmp_dump = NULL, *tmp = NULL;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+
+ this->mutex->lock (this->mutex);
+ if (id->dir == POLICY_FWD)
+ {
+ DBG1 (DBG_KNL, "policy FWD interface");
+ rv = SUCCESS;
+ goto error;
+ }
+ addr = id->dir == POLICY_IN ? data->dst : data->src;
+ for (int i = 0; i < N_RETRY_GET_IF; i++)
+ {
+ if (!charon->kernel->get_interface (charon->kernel, addr, &interface))
+ {
+ DBG1 (DBG_KNL, "policy no interface %H", addr);
+ free (interface);
+ interface = NULL;
+ sleep (1);
+ }
+
+ if (interface)
+ {
+ DBG1 (DBG_KNL, "policy have interface %H", addr);
+ break;
+ }
+ }
+ if (!interface)
+ goto error;
+
+ DBG2 (DBG_KNL, "manage policy [%s] interface [%s]", add ? "ADD" : "DEL",
+ interface);
+
+ spd = this->spds->get (this->spds, interface);
+ if (!spd)
+ {
+ if (!add)
+ {
+ DBG1 (DBG_KNL, "SPD for %s not found, should not be deleted",
+ interface);
+ goto error;
+ }
+ sw_if_index = get_sw_if_index (interface);
+ DBG1 (DBG_KNL, "firstly created, spd for %s found sw_if_index is %d",
+ interface, sw_if_index);
+ if (sw_if_index == ~0)
+ {
+ DBG1 (DBG_KNL, "sw_if_index for %s not found", interface);
+ goto error;
+ }
+ spd_id = ref_get (&this->next_spd_id);
+ if (spd_add_del (TRUE, spd_id))
+ {
+ DBG1 (DBG_KNL, "spd_add_del %d failed!!!!!", spd_id);
+ goto error;
+ }
+ if (interface_add_del_spd (TRUE, spd_id, sw_if_index))
+ {
+ DBG1 (DBG_KNL, "interface_add_del_spd %d %d failed!!!!!", spd_id,
+ sw_if_index);
+ goto error;
+ }
+ INIT (spd, .spd_id = spd_id, .sw_if_index = sw_if_index, .policy_num = 0,
+ .if_name = strdup (interface), );
+ this->spds->put (this->spds, spd->if_name, spd);
+ n_spd = true;
+ }
+
+ auto_priority = calculate_priority (data->prio, id->src_ts, id->dst_ts);
+ priority = data->manual_prio ? data->manual_prio : auto_priority;
+
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_entry_add_del_338b7411");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->is_add = add;
+ mp->entry.spd_id = htonl (spd->spd_id);
+ mp->entry.priority = htonl (priority - POLICY_PRIORITY_DEFAULT);
+ mp->entry.is_outbound = id->dir == POLICY_OUT;
+ switch (data->type)
+ {
+ case POLICY_IPSEC:
+ mp->entry.policy = htonl (IPSEC_API_SPD_ACTION_PROTECT);
+ break;
+ case POLICY_PASS:
+ mp->entry.policy = htonl (IPSEC_API_SPD_ACTION_BYPASS);
+ break;
+ case POLICY_DROP:
+ mp->entry.policy = htonl (IPSEC_API_SPD_ACTION_DISCARD);
+ break;
+ }
+ if ((data->type == POLICY_IPSEC) && data->sa)
+ {
+ kernel_ipsec_sa_id_t id = {
+ .src = data->src,
+ .dst = data->dst,
+ .proto = data->sa->esp.use ? IPPROTO_ESP : IPPROTO_AH,
+ .spi = data->sa->esp.use ? data->sa->esp.spi : data->sa->ah.spi,
+ };
+ sa_t *sa = NULL;
+ sa = this->sas->get (this->sas, &id);
+ if (!sa)
+ {
+ DBG1 (DBG_KNL, "SA ID not found");
+ goto error;
+ }
+ sad_id = sa->sa_id;
+ if (n_spd)
+ {
+ if (manage_bypass (TRUE, spd_id, ~0, priority))
+ {
+ DBG1 (DBG_KNL, "manage_bypass %d failed!!!!", spd_id);
+ goto error;
+ }
+ }
+ }
+
+ mp->entry.sa_id = htonl (sad_id);
+
+ bool is_ipv6 = false;
+ if (id->src_ts->get_type (id->src_ts) == TS_IPV6_ADDR_RANGE)
+ {
+ is_ipv6 = true;
+ mp->entry.local_address_start.af = htonl (ADDRESS_IP6);
+ mp->entry.local_address_stop.af = htonl (ADDRESS_IP6);
+ mp->entry.remote_address_start.af = htonl (ADDRESS_IP6);
+ mp->entry.remote_address_stop.af = htonl (ADDRESS_IP6);
+ }
+ else
+ {
+ mp->entry.local_address_start.af = htonl (ADDRESS_IP4);
+ mp->entry.local_address_stop.af = htonl (ADDRESS_IP4);
+ mp->entry.remote_address_start.af = htonl (ADDRESS_IP4);
+ mp->entry.remote_address_stop.af = htonl (ADDRESS_IP4);
+ }
+ mp->entry.protocol = id->src_ts->get_protocol (id->src_ts);
+
+ if (id->dir == POLICY_OUT)
+ {
+ src_from = id->src_ts->get_from_address (id->src_ts);
+ src_to = id->src_ts->get_to_address (id->src_ts);
+ src = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, src_to, 0);
+ dst_from = id->dst_ts->get_from_address (id->dst_ts);
+ dst_to = id->dst_ts->get_to_address (id->dst_ts);
+ dst = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, dst_to, 0);
+ }
+ else
+ {
+ dst_from = id->src_ts->get_from_address (id->src_ts);
+ dst_to = id->src_ts->get_to_address (id->src_ts);
+ dst = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, dst_from, 0);
+ src_from = id->dst_ts->get_from_address (id->dst_ts);
+ src_to = id->dst_ts->get_to_address (id->dst_ts);
+ src = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, src_from, 0);
+ }
+
+ if (src->is_anyaddr (src) && dst->is_anyaddr (dst))
+ {
+ memset (mp->entry.local_address_stop.un.ip6, 0xFF, 16);
+ memset (mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
+ }
+ else
+ {
+ memcpy (is_ipv6 ? mp->entry.local_address_start.un.ip6 :
+ mp->entry.local_address_start.un.ip4,
+ src_from.ptr, src_from.len);
+ memcpy (is_ipv6 ? mp->entry.local_address_stop.un.ip6 :
+ mp->entry.local_address_stop.un.ip4,
+ src_to.ptr, src_to.len);
+ memcpy (is_ipv6 ? mp->entry.remote_address_start.un.ip6 :
+ mp->entry.remote_address_start.un.ip4,
+ dst_from.ptr, dst_from.len);
+ memcpy (is_ipv6 ? mp->entry.remote_address_stop.un.ip6 :
+ mp->entry.remote_address_stop.un.ip4,
+ dst_to.ptr, dst_to.len);
+ }
+ mp->entry.local_port_start = htons (id->src_ts->get_from_port (id->src_ts));
+ mp->entry.local_port_stop = htons (id->src_ts->get_to_port (id->src_ts));
+ mp->entry.remote_port_start = htons (id->dst_ts->get_from_port (id->dst_ts));
+ mp->entry.remote_port_stop = htons (id->dst_ts->get_to_port (id->dst_ts));
+
+ /* check if policy exists in SPD */
+ mp_dump = vl_msg_api_alloc (sizeof (*mp_dump));
+ memset (mp_dump, 0, sizeof (*mp_dump));
+
+ msg_id = vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_dump_afefbf7d");
+ mp_dump->_vl_msg_id = htons (msg_id);
+ mp_dump->spd_id = htonl (spd->spd_id);
+ mp_dump->sa_id = htonl (sad_id);
+
+ if (vac->send_dump (vac, (char *) mp_dump, sizeof (*mp_dump), &out,
+ &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD lookup failed", add ? "adding" : "removing");
+ goto error;
+ }
+
+ int num = out_len / sizeof (*rmp_dump);
+ tmp = (void *) out;
+
+ /* found existing policy */
+ if (add && num)
+ {
+ int i;
+ for (i = 0; i < num; i++)
+ {
+ rmp_dump = tmp;
+ tmp += 1;
+ /* check if found entry equals the new one */
+ if (policy_equals (&mp->entry, &rmp_dump->entry))
+ goto next;
+ }
+ }
+ else if (!add && num == 0)
+ {
+ /* VPP doesn't have any policy to delete */
+ goto next;
+ }
+
+ free (out);
+
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+ ntohl (rmp->retval));
+ goto error;
+ }
+
+next:
+ if (add)
+ {
+ ref_get (&spd->policy_num);
+ }
+ else
+ {
+ if (ref_put (&spd->policy_num))
+ {
+ DBG1 (
+ DBG_KNL,
+ "policy_num's ref is 0, delete spd_id %d sw_if_index %d sad_id %x",
+ spd->spd_id, spd->sw_if_index, sad_id);
+ interface_add_del_spd (FALSE, spd->spd_id, spd->sw_if_index);
+ manage_bypass (FALSE, spd->spd_id, sad_id, priority);
+ spd_add_del (FALSE, spd->spd_id);
+ this->spds->remove (this->spds, interface);
+ if (spd->if_name)
+ {
+ free (spd->if_name);
+ spd->if_name = NULL;
+ }
+ if (spd)
+ {
+ free (spd);
+ spd = NULL;
+ }
+ }
+ }
+
+ if (this->install_routes && id->dir == POLICY_OUT && !mp->entry.protocol)
+ {
+ if (data->type == POLICY_IPSEC && data->sa->mode != MODE_TRANSPORT)
+ {
+ manage_route (this, add, id->dst_ts, data->src, data->dst);
+ }
+ }
+ rv = SUCCESS;
+error:
+ if (out != NULL)
+ free (out);
+ if (mp_dump != NULL)
+ vl_msg_api_free (mp_dump);
+ if (mp != NULL)
+ vl_msg_api_free (mp);
+ if (src != NULL)
+ src->destroy (src);
+ if (dst != NULL)
+ dst->destroy (dst);
+ if (interface != NULL)
+ free (interface);
+ this->mutex->unlock (this->mutex);
+ return rv;
+}
+
+METHOD (kernel_ipsec_t, get_features, kernel_feature_t,
+ private_kernel_vpp_ipsec_t *this)
+{
+ return KERNEL_ESP_V3_TFC;
+}
+
+METHOD (kernel_ipsec_t, get_spi, status_t, private_kernel_vpp_ipsec_t *this,
+ host_t *src, host_t *dst, uint8_t protocol, uint32_t *spi)
+{
+ static const u_int p = 268435399, offset = 0xc0000000;
+
+ *spi = htonl (offset + permute (ref_get (&this->nextspi) ^ this->mixspi, p));
+ return SUCCESS;
+}
+
+METHOD (kernel_ipsec_t, get_cpi, status_t, private_kernel_vpp_ipsec_t *this,
+ host_t *src, host_t *dst, uint16_t *cpi)
+{
+ DBG1 (DBG_KNL, "get_cpi is not supported!!!!!!!!!!!!!!!!!!!!!!!!");
+ return NOT_SUPPORTED;
+}
+
+/**
+ * Helper struct for expiration events
+ */
+typedef struct
+{
+
+ private_kernel_vpp_ipsec_t *manager;
+
+ kernel_ipsec_sa_id_t *sa_id;
+
+ /**
+ * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
+ */
+ uint32_t hard_offset;
+
+} vpp_sa_expired_t;
+
+/**
+ * Clean up expire data
+ */
+static void
+expire_data_destroy (vpp_sa_expired_t *data)
+{
+ free (data);
+}
+
+/**
+ * Callback for expiration events
+ */
+static job_requeue_t
+sa_expired (vpp_sa_expired_t *expired)
+{
+ private_kernel_vpp_ipsec_t *this = expired->manager;
+ sa_t *sa;
+ kernel_ipsec_sa_id_t *id = expired->sa_id;
+
+ this->mutex->lock (this->mutex);
+ sa = this->sas->get (this->sas, id);
+
+ if (sa)
+ {
+ charon->kernel->expire (charon->kernel, id->proto, id->spi, id->dst,
+ FALSE);
+ }
+
+ if (id->src)
+ id->src->destroy (id->src);
+ if (id->dst)
+ id->dst->destroy (id->dst);
+ free (id);
+ this->mutex->unlock (this->mutex);
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Schedule a job to handle IPsec SA expiration
+ */
+static void
+schedule_expiration (private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_add_sa_t *entry,
+ kernel_ipsec_sa_id_t *entry2)
+{
+ lifetime_cfg_t *lifetime = entry->lifetime;
+ vpp_sa_expired_t *expired;
+ callback_job_t *job;
+ uint32_t timeout;
+ kernel_ipsec_sa_id_t *id;
+
+ if (!lifetime->time.life)
+ { /* no expiration at all */
+ return;
+ }
+
+ INIT (id, .src = entry2->src->clone (entry2->src),
+ .dst = entry2->dst->clone (entry2->dst), .spi = entry2->spi,
+ .proto = entry2->proto, );
+
+ INIT (expired, .manager = this, .sa_id = id, );
+
+ /* schedule a rekey first, a hard timeout will be scheduled then, if any */
+ expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
+ timeout = lifetime->time.rekey;
+
+ if (lifetime->time.life <= lifetime->time.rekey || lifetime->time.rekey == 0)
+ { /* no rekey, schedule hard timeout */
+ expired->hard_offset = 0;
+ timeout = lifetime->time.life;
+ }
+
+ job =
+ callback_job_create ((callback_job_cb_t) sa_expired, expired,
+ (callback_job_cleanup_t) expire_data_destroy, NULL);
+ lib->scheduler->schedule_job (lib->scheduler, (job_t *) job, timeout);
+}
+
+METHOD (kernel_ipsec_t, add_sa, status_t, private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_sa_id_t *id, kernel_ipsec_add_sa_t *data)
+{
+ char *out = NULL;
+ int out_len;
+ vl_api_ipsec_sad_entry_add_del_t *mp;
+ vl_api_ipsec_sad_entry_add_del_reply_t *rmp;
+ uint32_t sad_id = ref_get (&this->next_sad_id);
+ uint8_t ca = 0, ia = 0;
+ status_t rv = FAILED;
+ chunk_t src, dst;
+ kernel_ipsec_sa_id_t *sa_id;
+ sa_t *sa;
+ int key_len = data->enc_key.len;
+
+ if ((data->enc_alg == ENCR_AES_CTR) ||
+ (data->enc_alg == ENCR_AES_GCM_ICV8) ||
+ (data->enc_alg == ENCR_AES_GCM_ICV12) ||
+ (data->enc_alg == ENCR_AES_GCM_ICV16))
+ {
+ static const int SALT_SIZE =
+ 4; /* See how enc_size is calculated at keymat_v2.derive_child_keys */
+ key_len = key_len - SALT_SIZE;
+ }
+ natt_port = lib->settings->get_int (
+ lib->settings, "%s.plugins.socket-default.natt", IKEV2_NATT_PORT, lib->ns);
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_sad_entry_add_del_ab64b5c6");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->is_add = 1;
+ mp->entry.sad_id = htonl (sad_id);
+ mp->entry.spi = id->spi;
+ mp->entry.protocol = id->proto == IPPROTO_ESP ? htonl (IPSEC_API_PROTO_ESP) :
+ htonl (IPSEC_API_PROTO_AH);
+
+ switch (data->enc_alg)
+ {
+ case ENCR_NULL:
+ ca = IPSEC_API_CRYPTO_ALG_NONE;
+ break;
+ case ENCR_AES_CBC:
+ switch (key_len * 8)
+ {
+ case 128:
+ ca = IPSEC_API_CRYPTO_ALG_AES_CBC_128;
+ break;
+ case 192:
+ ca = IPSEC_API_CRYPTO_ALG_AES_CBC_192;
+ break;
+ case 256:
+ ca = IPSEC_API_CRYPTO_ALG_AES_CBC_256;
+ break;
+ default:
+ DBG1 (DBG_KNL, "Key length %d is not supported by VPP!",
+ key_len * 8);
+ goto error;
+ }
+ break;
+ case ENCR_AES_CTR:
+ switch (key_len * 8)
+ {
+ case 128:
+ ca = IPSEC_API_CRYPTO_ALG_AES_CTR_128;
+ break;
+ case 192:
+ ca = IPSEC_API_CRYPTO_ALG_AES_CTR_192;
+ break;
+ case 256:
+ ca = IPSEC_API_CRYPTO_ALG_AES_CTR_256;
+ break;
+ default:
+ DBG1 (DBG_KNL, "Key length %d is not supported by VPP!",
+ key_len * 8);
+ goto error;
+ }
+ break;
+ case ENCR_AES_GCM_ICV8:
+ case ENCR_AES_GCM_ICV12:
+ case ENCR_AES_GCM_ICV16:
+ switch (key_len * 8)
+ {
+ case 128:
+ ca = IPSEC_API_CRYPTO_ALG_AES_GCM_128;
+ break;
+ case 192:
+ ca = IPSEC_API_CRYPTO_ALG_AES_GCM_192;
+ break;
+ case 256:
+ ca = IPSEC_API_CRYPTO_ALG_AES_GCM_256;
+ break;
+ default:
+ DBG1 (DBG_KNL, "Key length %d is not supported by VPP!",
+ key_len * 8);
+ goto error;
+ }
+ break;
+ case ENCR_DES:
+ ca = IPSEC_API_CRYPTO_ALG_DES_CBC;
+ break;
+ case ENCR_3DES:
+ ca = IPSEC_API_CRYPTO_ALG_3DES_CBC;
+ break;
+ default:
+ DBG1 (DBG_KNL, "algorithm %N not supported by VPP!",
+ encryption_algorithm_names, data->enc_alg);
+ goto error;
+ }
+ mp->entry.crypto_algorithm = htonl (ca);
+ mp->entry.crypto_key.length = key_len < 128 ? key_len : 128;
+ memcpy (mp->entry.crypto_key.data, data->enc_key.ptr,
+ mp->entry.crypto_key.length);
+
+ /* copy salt for AEAD algorithms */
+ if ((data->enc_alg == ENCR_AES_CTR) ||
+ (data->enc_alg == ENCR_AES_GCM_ICV8) ||
+ (data->enc_alg == ENCR_AES_GCM_ICV12) ||
+ (data->enc_alg == ENCR_AES_GCM_ICV16))
+ {
+ memcpy (&mp->entry.salt, data->enc_key.ptr + mp->entry.crypto_key.length,
+ 4);
+ }
+
+ switch (data->int_alg)
+ {
+ case AUTH_UNDEFINED:
+ ia = IPSEC_API_INTEG_ALG_NONE;
+ break;
+ case AUTH_HMAC_MD5_96:
+ ia = IPSEC_API_INTEG_ALG_MD5_96;
+ break;
+ case AUTH_HMAC_SHA1_96:
+ ia = IPSEC_API_INTEG_ALG_SHA1_96;
+ break;
+ case AUTH_HMAC_SHA2_256_96:
+ ia = IPSEC_API_INTEG_ALG_SHA_256_96;
+ break;
+ case AUTH_HMAC_SHA2_256_128:
+ ia = IPSEC_API_INTEG_ALG_SHA_256_128;
+ break;
+ case AUTH_HMAC_SHA2_384_192:
+ ia = IPSEC_API_INTEG_ALG_SHA_384_192;
+ break;
+ case AUTH_HMAC_SHA2_512_256:
+ ia = IPSEC_API_INTEG_ALG_SHA_512_256;
+ break;
+ default:
+ DBG1 (DBG_KNL, "algorithm %N not supported by VPP!",
+ integrity_algorithm_names, data->int_alg);
+ goto error;
+ break;
+ }
+ mp->entry.integrity_algorithm = htonl (ia);
+ mp->entry.integrity_key.length =
+ data->int_key.len < 128 ? data->int_key.len : 128;
+ memcpy (mp->entry.integrity_key.data, data->int_key.ptr,
+ mp->entry.integrity_key.length);
+
+ int flags = IPSEC_API_SAD_FLAG_NONE;
+ if (data->inbound)
+ flags |= IPSEC_API_SAD_FLAG_IS_INBOUND;
+ /* like the kernel-netlink plugin, anti-replay can be disabled with zero
+ * replay_window, but window size cannot be customized for vpp */
+ if (data->replay_window)
+ flags |= IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY;
+ if (data->esn)
+ flags |= IPSEC_API_SAD_FLAG_USE_ESN;
+ if (this->use_tunnel_mode_sa && data->mode == MODE_TUNNEL)
+ {
+ if (id->src->get_family (id->src) == AF_INET6)
+ flags |= IPSEC_API_SAD_FLAG_IS_TUNNEL_V6;
+ else
+ flags |= IPSEC_API_SAD_FLAG_IS_TUNNEL;
+ }
+ if (data->encap)
+ {
+ DBG1 (DBG_KNL, "UDP encap");
+ flags |= IPSEC_API_SAD_FLAG_UDP_ENCAP;
+ mp->entry.udp_src_port = htons (natt_port);
+ mp->entry.udp_dst_port = htons (natt_port);
+ }
+ mp->entry.flags = htonl (flags);
+
+ bool is_ipv6 = false;
+ if (id->src->get_family (id->src) == AF_INET6)
+ {
+ is_ipv6 = true;
+ mp->entry.tunnel_src.af = htonl (ADDRESS_IP6);
+ mp->entry.tunnel_dst.af = htonl (ADDRESS_IP6);
+ }
+ else
+ {
+ mp->entry.tunnel_src.af = htonl (ADDRESS_IP4);
+ mp->entry.tunnel_dst.af = htonl (ADDRESS_IP4);
+ }
+ src = id->src->get_address (id->src);
+ memcpy (is_ipv6 ? mp->entry.tunnel_src.un.ip6 : mp->entry.tunnel_src.un.ip4,
+ src.ptr, src.len);
+ dst = id->dst->get_address (id->dst);
+ memcpy (is_ipv6 ? mp->entry.tunnel_dst.un.ip6 : mp->entry.tunnel_dst.un.ip4,
+ dst.ptr, dst.len);
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac adding SA failed");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "add SA failed rv:%d", ntohl (rmp->retval));
+ goto error;
+ }
+
+ this->mutex->lock (this->mutex);
+ INIT (sa_id, .src = id->src->clone (id->src),
+ .dst = id->dst->clone (id->dst), .spi = id->spi, .proto = id->proto, );
+ INIT (sa, .sa_id = sad_id, .stat_index = ntohl (rmp->stat_index),
+ .sa_id_p = sa_id, );
+ DBG4 (DBG_KNL, "put sa by its sa_id %x !!!!!!", sad_id);
+ this->sas->put (this->sas, sa_id, sa);
+ schedule_expiration (this, data, id);
+ this->mutex->unlock (this->mutex);
+ rv = SUCCESS;
+
+error:
+ free (out);
+ vl_msg_api_free (mp);
+ return rv;
+}
+
+METHOD (kernel_ipsec_t, update_sa, status_t, private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_sa_id_t *id, kernel_ipsec_update_sa_t *data)
+{
+ DBG1 (DBG_KNL,
+ "update sa not supported!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ return NOT_SUPPORTED;
+}
+
+METHOD (kernel_ipsec_t, query_sa, status_t, private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_sa_id_t *id, kernel_ipsec_query_sa_t *data,
+ uint64_t *bytes, uint64_t *packets, time_t *time)
+{
+ status_t rv = FAILED;
+ sa_t *sa;
+ u32 *dir = NULL;
+ int i, k;
+ stat_segment_data_t *res = NULL;
+ u8 **pattern = 0;
+ uint64_t res_bytes = 0;
+ uint64_t res_packets = 0;
+
+ this->mutex->lock (this->mutex);
+ sa = this->sas->get (this->sas, id);
+ if (!sa)
+ {
+ this->mutex->unlock (this->mutex);
+ DBG1 (DBG_KNL, "SA not found");
+ return NOT_FOUND;
+ }
+
+ if (this->sm == NULL)
+ {
+ stat_client_main_t *sm = NULL;
+ sm = stat_client_get ();
+
+ if (!sm)
+ {
+ DBG1 (DBG_KNL, "Not connecting with stats segmentation");
+ this->mutex->unlock (this->mutex);
+ return NOT_FOUND;
+ }
+ this->sm = sm;
+ int rv_stat = stat_segment_connect_r ("/run/vpp/stats.sock", this->sm);
+ if (rv_stat != 0)
+ {
+ stat_client_free (this->sm);
+ this->sm = NULL;
+ DBG1 (DBG_KNL, "Not connecting with stats segmentation");
+ this->mutex->unlock (this->mutex);
+ return NOT_FOUND;
+ }
+ }
+
+ vec_add1 (pattern, (u8 *) "/net/ipsec/sa");
+ dir = stat_segment_ls_r ((u8 **) pattern, this->sm);
+ res = stat_segment_dump_r (dir, this->sm);
+ /* i-loop for each results find by pattern - here two:
+ * 1. /net/ipsec/sa
+ * 2. /net/ipsec/sa/lost
+ */
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ /* type for how many packets are lost */
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ if (res[i].simple_counter_vec == 0)
+ continue;
+ break;
+ /* type for counter for each SA */
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ if (res[i].combined_counter_vec == 0)
+ continue;
+ /* k-loop for each threads - that you run VPP */
+ for (k = 0; k < vec_len (res[i].combined_counter_vec); k++)
+ {
+ if (sa->stat_index <= vec_len (res[i].combined_counter_vec[k]))
+ {
+ DBG4 (DBG_KNL, "Thread: %d, Packets: %lu, Bytes: %lu", k,
+ res[i].combined_counter_vec[k][sa->stat_index].packets,
+ res[i].combined_counter_vec[k][sa->stat_index].bytes);
+ res_bytes +=
+ res[i].combined_counter_vec[k][sa->stat_index].bytes;
+ res_packets +=
+ res[i].combined_counter_vec[k][sa->stat_index].packets;
+ }
+ }
+ break;
+ case STAT_DIR_TYPE_NAME_VECTOR:
+ if (res[i].name_vector == 0)
+ continue;
+ break;
+ }
+ }
+
+ vec_free (pattern);
+ vec_free (dir);
+ stat_segment_data_free (res);
+
+ if (bytes)
+ {
+ *bytes = res_bytes;
+ }
+ if (packets)
+ {
+ *packets = res_packets;
+ }
+ if (time)
+ {
+ *time = 0;
+ }
+
+ this->mutex->unlock (this->mutex);
+ rv = SUCCESS;
+ return rv;
+}
+
+METHOD (kernel_ipsec_t, del_sa, status_t, private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_sa_id_t *id, kernel_ipsec_del_sa_t *data)
+{
+ char *out = NULL;
+ int out_len;
+ vl_api_ipsec_sad_entry_add_del_t *mp = NULL;
+ vl_api_ipsec_sad_entry_add_del_reply_t *rmp = NULL;
+ status_t rv = FAILED;
+ sa_t *sa;
+
+ this->mutex->lock (this->mutex);
+ sa = this->sas->get (this->sas, id);
+ if (!sa)
+ {
+ DBG1 (DBG_KNL, "SA not found");
+ rv = NOT_FOUND;
+ goto error;
+ }
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->is_add = 0;
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_sad_entry_add_del_ab64b5c6");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->entry.sad_id = htonl (sa->sa_id);
+
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "vac removing SA failed");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "del SA failed rv:%d", ntohl (rmp->retval));
+ goto error;
+ }
+
+ void *temp = this->sas->remove (this->sas, id);
+ if (sa->sa_id_p)
+ {
+ if (sa->sa_id_p->src)
+ sa->sa_id_p->src->destroy (sa->sa_id_p->src);
+ if (sa->sa_id_p->dst)
+ sa->sa_id_p->dst->destroy (sa->sa_id_p->dst);
+ free (sa->sa_id_p);
+ }
+ free (sa);
+ rv = SUCCESS;
+error:
+ free (out);
+ vl_msg_api_free (mp);
+ this->mutex->unlock (this->mutex);
+ return rv;
+}
+
+METHOD (kernel_ipsec_t, flush_sas, status_t, private_kernel_vpp_ipsec_t *this)
+{
+ enumerator_t *enumerator;
+ int out_len;
+ char *out;
+ vl_api_ipsec_sad_entry_add_del_t *mp = NULL;
+ vl_api_ipsec_sad_entry_add_del_reply_t *rmp = NULL;
+ sa_t *sa = NULL;
+ status_t rv = FAILED;
+
+ this->mutex->lock (this->mutex);
+ enumerator = this->sas->create_enumerator (this->sas);
+ while (enumerator->enumerate (enumerator, &sa))
+ {
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "ipsec_sad_entry_add_del_ab64b5c6");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->entry.sad_id = htonl (sa->sa_id);
+ mp->is_add = 0;
+ if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG1 (DBG_KNL, "flush_sas failed!!!!");
+ goto error;
+ }
+ rmp = (void *) out;
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "flush_sas failed!!!! rv: %d", ntohl (rmp->retval));
+ goto error;
+ }
+ if (sa->sa_id_p)
+ {
+ if (sa->sa_id_p->src)
+ sa->sa_id_p->src->destroy (sa->sa_id_p->src);
+ if (sa->sa_id_p->dst)
+ sa->sa_id_p->dst->destroy (sa->sa_id_p->dst);
+ }
+ free (out);
+ vl_msg_api_free (mp);
+ this->sas->remove_at (this->sas, enumerator);
+ free (sa->sa_id_p);
+ free (sa);
+ }
+ rv = SUCCESS;
+error:
+ if (out != NULL)
+ free (out);
+ if (mp != NULL)
+ vl_msg_api_free (mp);
+
+ enumerator->destroy (enumerator);
+ this->mutex->unlock (this->mutex);
+
+ return rv;
+}
+
+METHOD (kernel_ipsec_t, add_policy, status_t, private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_policy_id_t *id, kernel_ipsec_manage_policy_t *data)
+{
+ return manage_policy (this, TRUE, id, data);
+}
+
+METHOD (kernel_ipsec_t, query_policy, status_t,
+ private_kernel_vpp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
+ kernel_ipsec_query_policy_t *data, time_t *use_time)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD (kernel_ipsec_t, del_policy, status_t, private_kernel_vpp_ipsec_t *this,
+ kernel_ipsec_policy_id_t *id, kernel_ipsec_manage_policy_t *data)
+{
+ return manage_policy (this, FALSE, id, data);
+}
+
+METHOD (kernel_ipsec_t, flush_policies, status_t,
+ private_kernel_vpp_ipsec_t *this)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD (kernel_ipsec_t, bypass_socket, bool, private_kernel_vpp_ipsec_t *this,
+ int fd, int family)
+{
+ return FALSE;
+}
+
+METHOD (kernel_ipsec_t, enable_udp_decap, bool,
+ private_kernel_vpp_ipsec_t *this, int fd, int family, u_int16_t port)
+{
+ DBG1 (DBG_KNL, "enable_udp_decap not supported!!!!!!!!!!!!!!!!!!!!!!!!!");
+ return FALSE;
+}
+
+METHOD (kernel_ipsec_t, destroy, void, private_kernel_vpp_ipsec_t *this)
+{
+ this->mutex->destroy (this->mutex);
+ this->sas->destroy (this->sas);
+ this->spds->destroy (this->spds);
+ this->routes->destroy (this->routes);
+ if (this->sm)
+ {
+ stat_segment_disconnect_r (this->sm);
+ stat_client_free (this->sm);
+ this->sm = NULL;
+ }
+ free (this);
+}
+
+kernel_vpp_ipsec_t *
+kernel_vpp_ipsec_create ()
+{
+ private_kernel_vpp_ipsec_t *this;
+
+ INIT(this,
+ .public = {
+ .interface = {
+ .get_features = _get_features,
+ .get_spi = _get_spi,
+ .get_cpi = _get_cpi,
+ .add_sa = _add_sa,
+ .update_sa = _update_sa,
+ .query_sa = _query_sa,
+ .del_sa = _del_sa,
+ .flush_sas = _flush_sas,
+ .add_policy = _add_policy,
+ .query_policy = _query_policy,
+ .del_policy = _del_policy,
+ .flush_policies = _flush_policies,
+ .bypass_socket = _bypass_socket,
+ .enable_udp_decap = _enable_udp_decap,
+ .destroy = _destroy,
+ },
+ },
+ .next_sad_id = 0,
+ .next_spd_id = 0,
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .sas = hashtable_create((hashtable_hash_t)sa_hash,
+ (hashtable_equals_t)sa_equals, 32),
+ .spds = hashtable_create((hashtable_hash_t)interface_hash,
+ (hashtable_equals_t)interface_equals, 4),
+ .routes = linked_list_create(),
+ .install_routes = lib->settings->get_bool(lib->settings,
+ "%s.install_routes", TRUE, lib->ns),
+ .use_tunnel_mode_sa = lib->settings->get_bool(lib->settings,
+ "%s.plugins.kernel-vpp.use_tunnel_mode_sa",
+ TRUE, lib->ns),
+ .sm = NULL,
+ );
+
+ if (!init_spi (this))
+ {
+ destroy (this);
+ return NULL;
+ }
+
+ return &this->public;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h b/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h
new file mode 100644
index 00000000000..64669881574
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h
@@ -0,0 +1,41 @@
+#ifndef KERNEL_VPP_IPSEC_H_
+#define KERNEL_VPP_IPSEC_H_
+/*
+ * Copyright (c) 2022 Intel 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 <kernel/kernel_ipsec.h>
+
+typedef struct kernel_vpp_ipsec_t kernel_vpp_ipsec_t;
+
+/**
+ * Implementation of the kernel ipsec interface using Netlink.
+ */
+struct kernel_vpp_ipsec_t
+{
+
+ /**
+ * Implements kernel_ipsec_t interface
+ */
+ kernel_ipsec_t interface;
+};
+
+/**
+ * Create a vpp kernel ipsec interface instance.
+ *
+ * @return kernel_vpp_ipsec_t instance
+ */
+kernel_vpp_ipsec_t *kernel_vpp_ipsec_create ();
+
+#endif /** KERNEL_VPP_IPSEC_H_ @}*/
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_net.c b/extras/strongswan/vpp_sswan/kernel_vpp_net.c
new file mode 100644
index 00000000000..82eea1794f6
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_net.c
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 2022 Intel 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 <utils/debug.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <threading/thread.h>
+#include <threading/mutex.h>
+
+#define vl_typedefs
+#define vl_endianfun
+/* Include the (first) vlib-api API definition layer */
+#include <vlibmemory/vl_memory_api_h.h>
+/* Include the current layer (third) vpp API definition layer */
+#include <vpp/api/vpe_types.api.h>
+#include <vpp/api/vpe.api.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
+#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
+#include <vnet/ip/ip.api_enum.h>
+#include <vnet/ip/ip.api_types.h>
+#include <vnet/interface.api_enum.h>
+#include <vnet/interface.api_types.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+#include "kernel_vpp_net.h"
+#include "kernel_vpp_shared.h"
+
+typedef struct private_kernel_vpp_net_t private_kernel_vpp_net_t;
+
+/**
+ * Private data of kernel_vpp_net implementation.
+ */
+struct private_kernel_vpp_net_t
+{
+
+ /**
+ * Public interface.
+ */
+ kernel_vpp_net_t public;
+
+ /**
+ * Mutex to access interface list
+ */
+ mutex_t *mutex;
+
+ /**
+ * Known interfaces, as iface_t
+ */
+ linked_list_t *ifaces;
+
+ /**
+ * Inteface update thread
+ */
+ thread_t *net_update;
+
+ /**
+ * TRUE if interface events enabled
+ */
+ bool events_on;
+};
+
+/**
+ * Interface entry
+ */
+typedef struct
+{
+ /** interface index */
+ uint32_t index;
+ /** interface name */
+ char if_name[64];
+ /** list of known addresses, as host_t */
+ linked_list_t *addrs;
+ /** TRUE if up */
+ bool up;
+} iface_t;
+
+/**
+ * Address enumerator
+ */
+typedef struct
+{
+ /** implements enumerator_t */
+ enumerator_t public;
+ /** what kind of address should we enumerate? */
+ kernel_address_type_t which;
+ /** enumerator over interfaces */
+ enumerator_t *ifaces;
+ /** current enumerator over addresses, or NULL */
+ enumerator_t *addrs;
+ /** mutex to unlock on destruction */
+ mutex_t *mutex;
+} addr_enumerator_t;
+
+/**
+ * FIB path entry
+ */
+typedef struct
+{
+ chunk_t next_hop;
+ uint32_t sw_if_index;
+ uint8_t preference;
+} fib_path_t;
+
+/**
+ * Get an iface entry for a local address
+ */
+static iface_t *
+address2entry (private_kernel_vpp_net_t *this, host_t *ip)
+{
+ enumerator_t *ifaces, *addrs;
+ iface_t *entry, *found = NULL;
+ host_t *host;
+
+ ifaces = this->ifaces->create_enumerator (this->ifaces);
+ while (!found && ifaces->enumerate (ifaces, &entry))
+ {
+ addrs = entry->addrs->create_enumerator (entry->addrs);
+ while (!found && addrs->enumerate (addrs, &host))
+ {
+ if (host->ip_equals (host, ip))
+ {
+ found = entry;
+ }
+ }
+ addrs->destroy (addrs);
+ }
+ ifaces->destroy (ifaces);
+
+ return found;
+}
+
+/**
+ * Add or remove a route
+ */
+static status_t
+manage_route (private_kernel_vpp_net_t *this, bool add, chunk_t dst,
+ uint8_t prefixlen, host_t *gtw, char *name)
+{
+ char *out;
+ int out_len;
+ enumerator_t *enumerator;
+ iface_t *entry;
+ vl_api_ip_route_add_del_t *mp;
+ vl_api_ip_route_add_del_reply_t *rmp;
+ vl_api_fib_path_t *apath;
+ bool exists = FALSE;
+
+ for (int i = 0; i < N_RETRY_GET_IF; i++)
+ {
+ this->mutex->lock (this->mutex);
+ enumerator = this->ifaces->create_enumerator (this->ifaces);
+ while (enumerator->enumerate (enumerator, &entry))
+ {
+ if (streq (name, entry->if_name))
+ {
+ exists = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy (enumerator);
+ this->mutex->unlock (this->mutex);
+
+ if (!exists)
+ {
+ DBG1 (DBG_NET, "if_name %s not found", name);
+ sleep (1);
+ }
+ else
+ break;
+ }
+
+ if (!exists)
+ {
+ DBG1 (DBG_NET, "if_name %s not found", name);
+ return NOT_FOUND;
+ }
+
+ mp = vl_msg_api_alloc (sizeof (*mp) + sizeof (*apath));
+ memset (mp, 0, sizeof (*mp) + sizeof (*apath));
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_route_add_del_b8ecfe0d");
+ mp->_vl_msg_id = ntohs (msg_id);
+ mp->is_add = add;
+ mp->route.prefix.len = prefixlen;
+ mp->route.n_paths = 1;
+ apath = &mp->route.paths[0];
+ apath->sw_if_index = ntohl (entry->index);
+ apath->rpf_id = ~0;
+ apath->weight = 1;
+ switch (dst.len)
+ {
+ case 4:
+ mp->route.prefix.address.af = ntohl (ADDRESS_IP4);
+ memcpy (&mp->route.prefix.address.un.ip4, dst.ptr, dst.len);
+ if (gtw)
+ {
+ chunk_t addr = gtw->get_address (gtw);
+ apath->proto = ntohl (FIB_API_PATH_NH_PROTO_IP4);
+ memcpy (&apath->nh.address.ip4, addr.ptr, dst.len);
+ }
+ break;
+ case 16:
+ mp->route.prefix.address.af = ntohl (ADDRESS_IP6);
+ memcpy (&mp->route.prefix.address.un.ip6, dst.ptr, dst.len);
+ if (gtw)
+ {
+ chunk_t addr = gtw->get_address (gtw);
+ apath->proto = ntohl (FIB_API_PATH_NH_PROTO_IP6);
+ memcpy (&apath->nh.address.ip6, addr.ptr, dst.len);
+ }
+ break;
+ default:
+ vl_msg_api_free (mp);
+ return FAILED;
+ }
+
+ if (vac->send (vac, (char *) mp, sizeof (*mp) + sizeof (*apath), &out,
+ &out_len))
+ {
+ DBG1 (DBG_KNL, "vac %sing route failed", add ? "add" : "remov");
+ vl_msg_api_free (mp);
+ return FAILED;
+ }
+ rmp = (void *) out;
+ vl_msg_api_free (mp);
+ if (rmp->retval)
+ {
+ DBG1 (DBG_KNL, "%s route failed %d", add ? "add" : "delete",
+ ntohl (rmp->retval));
+ free (out);
+ return FAILED;
+ }
+ free (out);
+ return SUCCESS;
+}
+
+/**
+ * Check if an address or net (addr with prefix net bits) is in
+ * subnet (net with net_len net bits)
+ */
+static bool
+addr_in_subnet (chunk_t addr, int prefix, chunk_t net, int net_len)
+{
+ static const u_char mask[] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
+ };
+ int byte = 0;
+
+ if (net_len == 0)
+ { /* any address matches a /0 network */
+ return TRUE;
+ }
+ if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len)
+ {
+ return FALSE;
+ }
+ /* scan through all bytes in network order */
+ while (net_len > 0)
+ {
+ if (net_len < 8)
+ {
+ return (mask[net_len] & addr.ptr[byte]) ==
+ (mask[net_len] & net.ptr[byte]);
+ }
+ else
+ {
+ if (addr.ptr[byte] != net.ptr[byte])
+ {
+ return FALSE;
+ }
+ byte++;
+ net_len -= 8;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Get a route: If "nexthop" the nexthop is returned, source addr otherwise
+ */
+static host_t *
+get_route (private_kernel_vpp_net_t *this, host_t *dest, int prefix,
+ bool nexthop, char **iface, host_t *src)
+{
+ fib_path_t path;
+ char *out, *tmp;
+ int out_len, i, num;
+ vl_api_fib_path_t *fp;
+ host_t *addr = NULL;
+ enumerator_t *enumerator;
+ iface_t *entry;
+ int family;
+
+ path.sw_if_index = ~0;
+ path.preference = ~0;
+ path.next_hop = chunk_empty;
+
+ vl_api_ip_route_dump_t *mp;
+ vl_api_ip_route_details_t *rmp;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_route_dump_b9d2e09e");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->table.is_ip6 = dest->get_family (dest) == AF_INET6 ? 1 : 0;
+ if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ vl_msg_api_free (mp);
+ DBG2 (DBG_KNL, "send VL_API_IP_ROUTE_ADD_DEL failed");
+ return NULL;
+ }
+ vl_msg_api_free (mp);
+
+ if (dest->get_family (dest) == AF_INET)
+ {
+ i = 0;
+ family = AF_INET;
+ if (prefix == -1)
+ prefix = 32;
+
+ tmp = out;
+ while (tmp < (out + out_len))
+ {
+ rmp = (void *) tmp;
+ num = rmp->route.n_paths;
+
+ if (rmp->route.prefix.len &&
+ addr_in_subnet (
+ dest->get_address (dest), prefix,
+ chunk_create (rmp->route.prefix.address.un.ip4, 4),
+ rmp->route.prefix.len))
+ {
+ fp = rmp->route.paths;
+ for (i = 0; i < num; i++)
+ {
+#define IS_IP4_ANY(a) (a[0] == 0 && a[1] == 0 && a[2] == 0 & a[3] == 0)
+ if (fp->type == FIB_API_PATH_TYPE_DROP)
+ {
+ fp++;
+ continue;
+ }
+ if ((fp->preference < path.preference) ||
+ (path.sw_if_index == ~0) ||
+ IS_IP4_ANY (path.next_hop.ptr))
+ {
+ path.sw_if_index = ntohl (fp->sw_if_index);
+ path.preference = fp->preference;
+ if (path.next_hop.ptr)
+ vl_msg_api_free (path.next_hop.ptr);
+ path.next_hop = chunk_create (fp->nh.address.ip4, 4);
+ }
+ fp++;
+ }
+ }
+ tmp += sizeof (*rmp) + (sizeof (*fp) * num);
+ }
+ }
+ else
+ {
+ DBG1 (DBG_KNL, "not yet support ip6");
+ return NULL;
+ }
+
+ if (path.next_hop.len)
+ {
+ if (nexthop)
+ {
+ if (iface)
+ {
+ *iface = NULL;
+ this->mutex->lock (this->mutex);
+ enumerator = this->ifaces->create_enumerator (this->ifaces);
+ while (enumerator->enumerate (enumerator, &entry))
+ {
+ if (entry->index == path.sw_if_index)
+ {
+ *iface = strdup (entry->if_name);
+ break;
+ }
+ }
+ enumerator->destroy (enumerator);
+ this->mutex->unlock (this->mutex);
+ }
+ addr = host_create_from_chunk (family, path.next_hop, 0);
+ }
+ else
+ {
+ if (src)
+ {
+ addr = src->clone (src);
+ }
+ }
+ }
+
+ free (out);
+
+ return addr;
+}
+
+METHOD (enumerator_t, addr_enumerate, bool, addr_enumerator_t *this,
+ va_list args)
+{
+ iface_t *entry;
+ host_t **host;
+
+ VA_ARGS_VGET (args, host);
+
+ while (TRUE)
+ {
+ while (!this->addrs)
+ {
+ if (!this->ifaces->enumerate (this->ifaces, &entry))
+ {
+ return FALSE;
+ }
+ if (!entry->up && !(this->which & ADDR_TYPE_DOWN))
+ {
+ continue;
+ }
+ this->addrs = entry->addrs->create_enumerator (entry->addrs);
+ }
+ if (this->addrs->enumerate (this->addrs, host))
+ {
+ return TRUE;
+ }
+ this->addrs->destroy (this->addrs);
+ this->addrs = NULL;
+ }
+}
+
+METHOD (enumerator_t, addr_destroy, void, addr_enumerator_t *this)
+{
+ DESTROY_IF (this->addrs);
+ this->ifaces->destroy (this->ifaces);
+ this->mutex->unlock (this->mutex);
+ free (this);
+}
+
+METHOD (kernel_net_t, get_interface_name, bool, private_kernel_vpp_net_t *this,
+ host_t *ip, char **name)
+{
+ iface_t *entry;
+
+ this->mutex->lock (this->mutex);
+ entry = address2entry (this, ip);
+ if (entry && name)
+ {
+ *name = strdup (entry->if_name);
+ }
+ this->mutex->unlock (this->mutex);
+
+ return entry != NULL;
+}
+
+METHOD (kernel_net_t, create_address_enumerator, enumerator_t *,
+ private_kernel_vpp_net_t *this, kernel_address_type_t which)
+{
+ addr_enumerator_t *enumerator;
+
+ if (!(which & ADDR_TYPE_REGULAR))
+ {
+ /* we currently have no virtual, but regular IPs only */
+ return enumerator_create_empty ();
+ }
+
+ this->mutex->lock (this->mutex);
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = enumerator_enumerate_default,
+ .venumerate = _addr_enumerate,
+ .destroy = _addr_destroy,
+ },
+ .which = which,
+ .ifaces = this->ifaces->create_enumerator(this->ifaces),
+ .mutex = this->mutex,
+ );
+ return &enumerator->public;
+}
+
+METHOD (kernel_net_t, get_source_addr, host_t *,
+ private_kernel_vpp_net_t *this, host_t *dest, host_t *src)
+{
+ return get_route (this, dest, -1, FALSE, NULL, src);
+}
+
+METHOD (kernel_net_t, get_nexthop, host_t *, private_kernel_vpp_net_t *this,
+ host_t *dest, int prefix, host_t *src, char **iface)
+{
+ return get_route (this, dest, prefix, TRUE, iface, src);
+}
+
+METHOD (kernel_net_t, add_ip, status_t, private_kernel_vpp_net_t *this,
+ host_t *virtual_ip, int prefix, char *iface_name)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD (kernel_net_t, del_ip, status_t, private_kernel_vpp_net_t *this,
+ host_t *virtual_ip, int prefix, bool wait)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD (kernel_net_t, add_route, status_t, private_kernel_vpp_net_t *this,
+ chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip,
+ char *if_name)
+{
+ return manage_route (this, TRUE, dst_net, prefixlen, gateway, if_name);
+}
+
+METHOD (kernel_net_t, del_route, status_t, private_kernel_vpp_net_t *this,
+ chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip,
+ char *if_name)
+{
+ return manage_route (this, FALSE, dst_net, prefixlen, gateway, if_name);
+}
+
+static void
+iface_destroy (iface_t *this)
+{
+ this->addrs->destroy_offset (this->addrs, offsetof (host_t, destroy));
+ free (this);
+}
+
+METHOD (kernel_net_t, destroy, void, private_kernel_vpp_net_t *this)
+{
+ this->net_update->cancel (this->net_update);
+ this->mutex->destroy (this->mutex);
+ this->ifaces->destroy_function (this->ifaces, (void *) iface_destroy);
+ free (this);
+}
+
+/**
+ * Update addresses for an iface entry
+ */
+static void
+update_addrs (private_kernel_vpp_net_t *this, iface_t *entry)
+{
+ char *out;
+ int out_len, i, num;
+ vl_api_ip_address_dump_t *mp;
+ vl_api_ip_address_details_t *rmp, *tmp;
+ linked_list_t *addrs;
+ host_t *host;
+ enumerator_t *enumerator;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_address_dump_2d033de4");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->sw_if_index = htonl (entry->index);
+ mp->is_ipv6 = 0;
+ if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG2 (DBG_NET, "update_addrs : send VL_API_IP_ADDRESS_DUMP ipv4 failed");
+ vl_msg_api_free (mp);
+ return;
+ }
+ num = out_len / sizeof (*rmp);
+ addrs = linked_list_create ();
+ tmp = (vl_api_ip_address_details_t *) out;
+ for (i = 0; i < num; i++)
+ {
+ rmp = tmp;
+ tmp += 1;
+ host = host_create_from_chunk (
+ AF_INET, chunk_create (rmp->prefix.address.un.ip4, 4), 0);
+ addrs->insert_last (addrs, host);
+ }
+ free (out);
+
+ mp->is_ipv6 = 1;
+ if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+ {
+ DBG2 (DBG_NET, "update_addrs : send VL_API_IP_ADDRESS_DUMP ipv6 failed");
+ vl_msg_api_free (mp);
+ return;
+ }
+ num = out_len / sizeof (*rmp);
+ tmp = (vl_api_ip_address_details_t *) out;
+ for (i = 0; i < num; i++)
+ {
+ rmp = tmp;
+ tmp += 1;
+ host = host_create_from_chunk (
+ AF_INET6, chunk_create (rmp->prefix.address.un.ip6, 16), 0);
+ addrs->insert_last (addrs, host);
+ }
+ vl_msg_api_free (mp);
+ free (out);
+
+ /* clean-up */
+ enumerator = entry->addrs->create_enumerator (entry->addrs);
+ while (enumerator->enumerate (enumerator, &host))
+ {
+ host->destroy (host);
+ }
+ enumerator->destroy (enumerator);
+ entry->addrs->destroy (entry->addrs);
+ entry->addrs =
+ linked_list_create_from_enumerator (addrs->create_enumerator (addrs));
+ addrs->destroy (addrs);
+}
+
+/**
+ * VPP API interface event callback
+ */
+static void
+event_cb (char *data, int data_len, void *ctx)
+{
+ private_kernel_vpp_net_t *this = ctx;
+ vl_api_sw_interface_event_t *event;
+ vl_api_if_status_flags_t flags;
+ iface_t *entry;
+ enumerator_t *enumerator;
+
+ event = (void *) data;
+ this->mutex->lock (this->mutex);
+ enumerator = this->ifaces->create_enumerator (this->ifaces);
+ while (enumerator->enumerate (enumerator, &entry))
+ {
+ if (entry->index == ntohl (event->sw_if_index))
+ {
+ flags = ntohl (event->flags);
+ if (event->deleted)
+ {
+ this->ifaces->remove_at (this->ifaces, enumerator);
+ DBG2 (DBG_NET, "interface deleted %u %s", entry->index,
+ entry->if_name);
+ iface_destroy (entry);
+ }
+ else if (entry->up != (flags & IF_STATUS_API_FLAG_LINK_UP))
+ {
+ entry->up = (flags & IF_STATUS_API_FLAG_LINK_UP) ? TRUE : FALSE;
+ DBG2 (DBG_NET, "interface state changed %u %s %s", entry->index,
+ entry->if_name, entry->up ? "UP" : "DOWN");
+ }
+ break;
+ }
+ }
+ enumerator->destroy (enumerator);
+ this->mutex->unlock (this->mutex);
+}
+
+/**
+ * Inteface update thread (update interface list and interface address)
+ */
+static void *
+net_update_thread_fn (private_kernel_vpp_net_t *this)
+{
+ status_t rv;
+ while (1)
+ {
+ char *out;
+ int out_len;
+ vl_api_sw_interface_dump_t *mp;
+ vl_api_sw_interface_details_t *rmp;
+ enumerator_t *enumerator;
+ iface_t *entry;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "sw_interface_dump_aa610c27");
+ mp->_vl_msg_id = htons (msg_id);
+ mp->name_filter_valid = 0;
+ rv = vac->send_dump (vac, (u8 *) mp, sizeof (*mp), &out, &out_len);
+ if (!rv)
+ {
+ int i, num;
+ this->mutex->lock (this->mutex);
+ enumerator = this->ifaces->create_enumerator (this->ifaces);
+ num = out_len / sizeof (*rmp);
+ rmp = (vl_api_sw_interface_details_t *) out;
+ for (i = 0; i < num; i++)
+ {
+ bool exists = FALSE;
+ if (i)
+ rmp += 1;
+ while (enumerator->enumerate (enumerator, &entry))
+ {
+ if (entry->index == ntohl (rmp->sw_if_index))
+ {
+ exists = TRUE;
+ break;
+ }
+ }
+ if (!exists)
+ {
+ INIT (entry, .index = ntohl (rmp->sw_if_index),
+ .up = (rmp->flags & IF_STATUS_API_FLAG_LINK_UP) ?
+ TRUE :
+ FALSE,
+ .addrs = linked_list_create (), );
+ memcpy (entry->if_name, rmp->interface_name, 63);
+ this->ifaces->insert_last (this->ifaces, entry);
+ }
+ update_addrs (this, entry);
+ }
+ enumerator->destroy (enumerator);
+ this->mutex->unlock (this->mutex);
+ free (out);
+ }
+ vl_msg_api_free (mp);
+
+ if (!this->events_on)
+ {
+ vl_api_want_interface_events_t *emp;
+ api_main_t *am = vlibapi_get_main ();
+
+ emp = vl_msg_api_alloc (sizeof (*emp));
+ clib_memset (emp, 0, sizeof (*emp));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "want_interface_events_476f5a08");
+ emp->_vl_msg_id = ntohs (msg_id);
+ emp->enable_disable = 1;
+ emp->pid = ntohl (am->our_pid);
+ u16 msg_id_sw_interface_event =
+ vl_msg_api_get_msg_index ((u8 *) "sw_interface_event_2d3d95a7");
+ rv = vac->register_event (vac, (char *) emp, sizeof (*emp), event_cb,
+ msg_id_sw_interface_event, this);
+ if (!rv)
+ this->events_on = TRUE;
+ }
+
+ sleep (2);
+ }
+ return NULL;
+}
+
+kernel_vpp_net_t *
+kernel_vpp_net_create ()
+{
+ private_kernel_vpp_net_t *this;
+
+ INIT(this,
+ .public = {
+ .interface = {
+ .get_interface = _get_interface_name,
+ .create_address_enumerator = _create_address_enumerator,
+ .get_source_addr = _get_source_addr,
+ .get_nexthop = _get_nexthop,
+ .add_ip = _add_ip,
+ .del_ip = _del_ip,
+ .add_route = _add_route,
+ .del_route = _del_route,
+ .destroy = _destroy,
+ },
+ },
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .ifaces = linked_list_create(),
+ .events_on = FALSE,
+ );
+
+ this->net_update =
+ thread_create ((thread_main_t) net_update_thread_fn, this);
+
+ return &this->public;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_net.h b/extras/strongswan/vpp_sswan/kernel_vpp_net.h
new file mode 100644
index 00000000000..bf5359c29ed
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_net.h
@@ -0,0 +1,41 @@
+#ifndef KERNEL_VPP_NET_H_
+#define KERNEL_VPP_NET_H_
+/*
+ * Copyright (c) 2022 Intel 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 <kernel/kernel_net.h>
+
+typedef struct kernel_vpp_net_t kernel_vpp_net_t;
+
+/**
+ * Implementation of the kernel network interface using Netlink.
+ */
+struct kernel_vpp_net_t
+{
+
+ /**
+ * Implements kernel_net_t interface
+ */
+ kernel_net_t interface;
+};
+
+/**
+ * Create a vpp kernel network interface instance.
+ *
+ * @return kernel_vpp_net_t instance
+ */
+kernel_vpp_net_t *kernel_vpp_net_create ();
+
+#endif /** KERNEL_VPP_NET_H_ @}*/
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_plugin.c b/extras/strongswan/vpp_sswan/kernel_vpp_plugin.c
new file mode 100644
index 00000000000..9791987f5ae
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_plugin.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2022 Intel 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 <utils/debug.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#define vl_typedefs
+#define vl_endianfun
+/* Include the (first) vlib-api API definition layer */
+#include <vlibmemory/vl_memory_api_h.h>
+/* Include the current layer (third) vpp API definition layer */
+#include <vpp/api/vpe_types.api.h>
+#include <vpp/api/vpe.api.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+#include "kernel_vpp_plugin.h"
+#include "kernel_vpp_shared.h"
+#include "kernel_vpp_ipsec.h"
+#include "kernel_vpp_net.h"
+
+typedef struct private_kernel_vpp_plugin_t private_kernel_vpp_plugin_t;
+
+/**
+ * private data of kernel vpp plugin
+ */
+struct private_kernel_vpp_plugin_t
+{
+ /**
+ * implements plugin interface
+ */
+ kernel_vpp_plugin_t public;
+
+ vac_t *vac;
+};
+
+METHOD (plugin_t, get_name, char *, private_kernel_vpp_plugin_t *this)
+{
+ return "kernel-vpp";
+}
+
+METHOD (plugin_t, get_features, int, private_kernel_vpp_plugin_t *this,
+ plugin_feature_t *features[])
+{
+ static plugin_feature_t f[] = {
+ PLUGIN_CALLBACK (kernel_ipsec_register, kernel_vpp_ipsec_create),
+ PLUGIN_PROVIDE (CUSTOM, "kernel-ipsec"),
+ PLUGIN_CALLBACK (kernel_net_register, kernel_vpp_net_create),
+ PLUGIN_PROVIDE (CUSTOM, "kernel-net"),
+ };
+ *features = f;
+ return countof (f);
+}
+
+METHOD (plugin_t, destroy, void, private_kernel_vpp_plugin_t *this)
+{
+ if (this->vac)
+ {
+ lib->set (lib, "kernel-vpp-vac", NULL);
+ this->vac->destroy (this->vac);
+ }
+ free (this);
+}
+
+plugin_t *
+kernel_vpp_plugin_create ()
+{
+ private_kernel_vpp_plugin_t *this;
+
+ INIT(this,
+ .public = {
+ .plugin = {
+ .get_name = _get_name,
+ .get_features = _get_features,
+ .destroy = _destroy,
+ },
+ },
+ );
+
+ this->vac = vac_create ("strongswan");
+ if (!this->vac)
+ {
+ DBG1 (DBG_KNL, "vac_create failed");
+ destroy (this);
+ return NULL;
+ }
+ lib->set (lib, "kernel-vpp-vac", this->vac);
+
+ return &this->public.plugin;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_plugin.h b/extras/strongswan/vpp_sswan/kernel_vpp_plugin.h
new file mode 100644
index 00000000000..0214029afad
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_plugin.h
@@ -0,0 +1,34 @@
+#ifndef KERNEL_VPP_PLUGIN_H_
+#define KERNEL_VPP_PLUGIN_H_
+/*
+ * Copyright (c) 2022 Intel 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 <plugins/plugin.h>
+
+typedef struct kernel_vpp_plugin_t kernel_vpp_plugin_t;
+
+/**
+ * vpp kernel interface plugin
+ */
+struct kernel_vpp_plugin_t
+{
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+#endif /** KERNEL_VPP_PLUGIN_H_ @}*/
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_shared.c b/extras/strongswan/vpp_sswan/kernel_vpp_shared.c
new file mode 100644
index 00000000000..e958866b853
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_shared.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2022 Intel 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 <library.h>
+#include <utils/debug.h>
+#include <threading/thread.h>
+#include <threading/condvar.h>
+#include <threading/mutex.h>
+#include <collections/array.h>
+#include <collections/hashtable.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibmemory/memclnt.api_enum.h>
+
+#include "kernel_vpp_shared.h"
+
+#define vl_typedefs
+#define vl_endianfun
+/* Include the (first) vlib-api API definition layer */
+#include <vlibmemory/vl_memory_api_h.h>
+/* Include the current layer (third) vpp API definition layer */
+#include <vpp/api/vpe_types.api.h>
+#include <vpp/api/vpe.api.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+typedef struct private_vac_t private_vac_t;
+typedef struct vl_api_header_t vl_api_header_t;
+typedef struct vl_api_rheader_t vl_api_rheader_t;
+typedef struct want_event_reply_t want_event_reply_t;
+
+vac_t *vac;
+
+/**
+ * Private variables and functions of vac_t class.
+ */
+struct private_vac_t
+{
+
+ /**
+ * public part of the vac_t object.
+ */
+ vac_t public;
+
+ /**
+ * Timeout for VPP API replies, in ms
+ */
+ uint16_t read_timeout;
+
+ /**
+ * True if connected to VPP vlib
+ */
+ bool connected_to_vlib;
+
+ /**
+ * True if receive thread is running
+ */
+ bool rx_is_running;
+
+ /**
+ * Receive thread
+ */
+ thread_t *rx;
+
+ /**
+ * Mutex to lock receive queue
+ */
+ mutex_t *queue_lock;
+
+ /**
+ * Condition variable rx thread susspend
+ */
+ condvar_t *suspend_cv;
+
+ /**
+ * Condition variable rx thread resume
+ */
+ condvar_t *resume_cv;
+
+ /**
+ * Condition variable rx thread terminate
+ */
+ condvar_t *terminate_cv;
+
+ /**
+ * Mutex to lock send VPP API message entries
+ */
+ mutex_t *entries_lock;
+
+ /**
+ * VPP API message entries currently active, uintptr_t seq => entry_t
+ */
+ hashtable_t *entries;
+
+ /**
+ * Mutex to lock VPP API event entries
+ */
+ mutex_t *events_lock;
+
+ /**
+ * VPP API event entries currently active, uintptr_t id = event_t
+ */
+ hashtable_t *events;
+
+ /**
+ * Current sequence number for VPP API messages
+ */
+ refcount_t seq;
+};
+
+/**
+ * VPP API message header
+ */
+struct vl_api_header_t
+{
+
+ /** message ID */
+ uint16_t _vl_msg_id;
+
+ /** opaque cookie to identify the client */
+ uint32_t client_index;
+
+ /** client context, to match reply with request */
+ uint32_t context;
+} __attribute__ ((packed));
+
+/**
+ * VPP API response message header
+ */
+struct vl_api_rheader_t
+{
+
+ /** message ID */
+ uint16_t _vl_msg_id;
+
+ /** opaque cookie to identify the client */
+ uint32_t context;
+} __attribute__ ((packed));
+
+/**
+ * VPP API register event response message header
+ */
+struct want_event_reply_t
+{
+
+ /** message ID */
+ uint16_t _vl_msg_id;
+
+ /** opaque cookie to identify the client */
+ uint32_t context;
+
+ /** retrun code for the request */
+ int32_t retval;
+} __attribute__ ((packed));
+
+/**
+ * VPP API request entry the answer for a waiting thread is collected in
+ */
+typedef struct
+{
+ /** Condition variable thread is waiting */
+ condvar_t *condvar;
+ /** Array of reply msgs in a multi-message response, as struct rmsgbuf_t */
+ array_t *rmsgs;
+ /** All response messages received? */
+ bool complete;
+ /** Is VPP API dump? */
+ bool is_dump;
+} entry_t;
+
+/**
+ * Reply message buffer
+ */
+typedef struct
+{
+ /** Data length */
+ uint32_t data_len;
+ /** Reply data */
+ uint8_t data[0];
+} rmsgbuf_t;
+
+/**
+ * VPP API event entry
+ */
+typedef struct
+{
+ /** Event callback */
+ event_cb_t cb;
+ /** User data passed to callback */
+ void *ctx;
+} event_t;
+
+/**
+ * Free VPP API message
+ */
+static void
+vac_free (void *msg)
+{
+ vl_msg_api_free (msg);
+}
+
+/**
+ * Process a single VPP API message
+ */
+static void
+vac_api_handler (private_vac_t *this, void *msg)
+{
+ vl_api_rheader_t *rmp;
+ entry_t *entry;
+ rmsgbuf_t *rmsg;
+ uintptr_t seq, event_id;
+ u16 id = ntohs (*((u16 *) msg));
+ msgbuf_t *msgbuf = (msgbuf_t *) (((u8 *) msg) - offsetof (msgbuf_t, data));
+ int l = ntohl (msgbuf->data_len);
+ event_t *event;
+
+ if (l == 0)
+ {
+ DBG2 (DBG_KNL, "vac msg ID %d has wrong len %d", id, l);
+ vac_free (msg);
+ return;
+ }
+
+ rmp = (void *) msg;
+ seq = (uintptr_t) rmp->context;
+
+ this->entries_lock->lock (this->entries_lock);
+ entry = this->entries->get (this->entries, (void *) seq);
+ if (entry)
+ {
+ if (entry->is_dump)
+ {
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "control_ping_reply_f6b0b8ca");
+ if (id == msg_id)
+ {
+ entry->complete = TRUE;
+ entry->condvar->signal (entry->condvar);
+ vac_free (msg);
+ this->entries_lock->unlock (this->entries_lock);
+ return;
+ }
+ }
+ else
+ {
+ entry->complete = TRUE;
+ entry->condvar->signal (entry->condvar);
+ }
+
+ rmsg = malloc (l + sizeof (msgbuf_t));
+ rmsg->data_len = l;
+ memcpy (rmsg->data, msg, l);
+ array_insert (entry->rmsgs, ARRAY_TAIL, rmsg);
+ }
+ else
+ {
+ this->events_lock->lock (this->events_lock);
+ event_id = (uintptr_t) id;
+ event = this->events->get (this->events, (void *) event_id);
+ if (event)
+ event->cb (msg, l, event->ctx);
+ else
+ DBG1 (DBG_KNL, "received unknown vac msg seq %u id %d len %d, ignored",
+ seq, id, l);
+ this->events_lock->unlock (this->events_lock);
+ }
+
+ this->entries_lock->unlock (this->entries_lock);
+ vac_free (msg);
+}
+
+/**
+ * VPP API receive thread
+ */
+static void *
+vac_rx_thread_fn (private_vac_t *this)
+{
+ svm_queue_t *q;
+ api_main_t *am = vlibapi_get_main ();
+ vl_api_memclnt_keepalive_t *mp;
+ vl_api_memclnt_keepalive_reply_t *rmp;
+ vl_shmem_hdr_t *shmem_hdr;
+ uword msg;
+
+ q = am->vl_input_queue;
+
+ const u16 msg_id_rx_thread_exit =
+ vl_msg_api_get_msg_index ((u8 *) "rx_thread_exit_c3a3a452");
+ const u16 msg_id_memclnt_rx_thread_suspend =
+ vl_msg_api_get_msg_index ((u8 *) "memclnt_rx_thread_suspend_c3a3a452");
+ const u16 msg_id_memclnt_read_timeout =
+ vl_msg_api_get_msg_index ((u8 *) "memclnt_read_timeout_c3a3a452");
+ const u16 msg_id_memclnt_keepalive =
+ vl_msg_api_get_msg_index ((u8 *) "memclnt_keepalive_51077d14");
+
+ while (TRUE)
+ {
+ while (!svm_queue_sub (q, (u8 *) &msg, SVM_Q_WAIT, 0))
+ {
+ u16 id = ntohs (*((u16 *) msg));
+
+ if (msg_id_rx_thread_exit == id)
+ {
+ vl_msg_api_free ((void *) msg);
+ this->queue_lock->lock (this->queue_lock);
+ this->terminate_cv->signal (this->terminate_cv);
+ this->queue_lock->unlock (this->queue_lock);
+ DBG3 (DBG_KNL, "vac received rx thread exit [%d]",
+ msg_id_rx_thread_exit);
+ thread_exit (NULL);
+ return NULL;
+ }
+ else if (msg_id_memclnt_rx_thread_suspend == id)
+ {
+ vl_msg_api_free ((void *) msg);
+ this->queue_lock->lock (this->queue_lock);
+ this->suspend_cv->signal (this->suspend_cv);
+ this->resume_cv->wait (this->resume_cv, this->queue_lock);
+ this->queue_lock->unlock (this->queue_lock);
+ DBG3 (DBG_KNL, "vac received rx thread suspend [%d]",
+ msg_id_memclnt_rx_thread_suspend);
+ }
+ else if (msg_id_memclnt_read_timeout == id)
+ {
+ DBG3 (DBG_KNL, "vac received read timeout [%d]",
+ msg_id_memclnt_read_timeout);
+ vl_msg_api_free ((void *) msg);
+ }
+ else if (msg_id_memclnt_keepalive == id)
+ {
+ mp = (void *) msg;
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ memset (rmp, 0, sizeof (*rmp));
+ u16 msg_id = vl_msg_api_get_msg_index (
+ (u8 *) "memclnt_keepalive_reply_e8d4e804");
+ rmp->_vl_msg_id = ntohs (msg_id);
+ rmp->context = mp->context;
+ shmem_hdr = am->shmem_hdr;
+ vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &rmp);
+ vl_msg_api_free ((void *) msg);
+ DBG3 (DBG_KNL, "vac received keepalive %d",
+ msg_id_memclnt_keepalive);
+ }
+ else
+ vac_api_handler (this, (void *) msg);
+ }
+ }
+
+ return NULL;
+}
+
+METHOD (vac_t, destroy, void, private_vac_t *this)
+{
+ if (this->connected_to_vlib)
+ {
+ if (this->rx)
+ {
+ api_main_t *am = vlibapi_get_main ();
+ vl_api_rx_thread_exit_t *ep;
+ bool timed_out;
+ ep = vl_msg_api_alloc (sizeof (*ep));
+ memset (ep, 0, sizeof (*ep));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "rx_thread_exit_c3a3a452");
+ ep->_vl_msg_id = ntohs (msg_id);
+ vl_msg_api_send_shmem (am->vl_input_queue, (u8 *) &ep);
+ this->queue_lock->lock (this->queue_lock);
+ timed_out = this->terminate_cv->timed_wait (this->terminate_cv,
+ this->queue_lock, 5000);
+ this->queue_lock->unlock (this->queue_lock);
+ if (timed_out)
+ this->rx->cancel (this->rx);
+ else
+ this->rx->join (this->rx);
+ }
+ vl_client_disconnect ();
+ vl_client_api_unmap ();
+ }
+
+ this->queue_lock->destroy (this->queue_lock);
+ this->suspend_cv->destroy (this->suspend_cv);
+ this->resume_cv->destroy (this->resume_cv);
+ this->terminate_cv->destroy (this->terminate_cv);
+ this->entries->destroy (this->entries);
+ this->entries_lock->destroy (this->entries_lock);
+ this->events->destroy (this->events);
+ this->events_lock->destroy (this->events_lock);
+
+ vac = NULL;
+ free (this);
+}
+
+/**
+ * Write a VPP API message to shared memory
+ */
+static status_t
+vac_write (private_vac_t *this, char *p, int l, uint32_t ctx)
+{
+ api_main_t *am = vlibapi_get_main ();
+ vl_api_header_t *mp = vl_msg_api_alloc (l);
+ memset (mp, 0, sizeof (*mp));
+ svm_queue_t *q;
+
+ if (!this->connected_to_vlib)
+ return FAILED;
+
+ if (!mp)
+ return FAILED;
+
+ memcpy (mp, p, l);
+ mp->client_index = am->my_client_index;
+ mp->context = ctx;
+ q = am->shmem_hdr->vl_input_queue;
+ if (svm_queue_add (q, (u8 *) &mp, 0))
+ {
+ DBG1 (DBG_KNL, "vac vpe_api_write failed");
+ vac_free (mp);
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Clean up a thread waiting entry
+ */
+static void
+destroy_entry (entry_t *entry)
+{
+ entry->condvar->destroy (entry->condvar);
+ array_destroy_function (entry->rmsgs, (void *) free, NULL);
+ free (entry);
+}
+
+/**
+ * Send VPP API message and wait for a reply
+ */
+static status_t
+send_vac (private_vac_t *this, char *in, int in_len, char **out, int *out_len,
+ bool is_dump)
+{
+ entry_t *entry;
+ uint32_t ctx = ref_get (&this->seq);
+ uintptr_t seq = (uintptr_t) ctx;
+ rmsgbuf_t *rmsg;
+ char *ptr;
+ int i;
+
+ this->entries_lock->lock (this->entries_lock);
+ INIT (entry, .condvar = condvar_create (CONDVAR_TYPE_DEFAULT),
+ .rmsgs = array_create (0, 0), .is_dump = is_dump, );
+ this->entries->put (this->entries, (void *) seq, entry);
+
+ if (vac_write (this, in, in_len, ctx))
+ {
+ destroy_entry (entry);
+ this->entries_lock->unlock (this->entries_lock);
+ return FAILED;
+ }
+
+ if (is_dump)
+ {
+ vl_api_control_ping_t *mp;
+ status_t rv;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "control_ping_51077d14");
+ mp->_vl_msg_id = ntohs (msg_id);
+ rv = vac_write (this, (char *) mp, sizeof (*mp), ctx);
+ vl_msg_api_free (mp);
+ if (rv)
+ {
+ DBG2 (DBG_KNL, "vac_write VL_API_CONTROL_PING failed");
+ destroy_entry (entry);
+ this->entries_lock->unlock (this->entries_lock);
+ return FAILED;
+ }
+ }
+
+ while (!entry->complete)
+ {
+ if (this->read_timeout)
+ {
+ if (entry->condvar->timed_wait (entry->condvar, this->entries_lock,
+ this->read_timeout * 1000))
+ {
+ break;
+ }
+ }
+ else
+ {
+ entry->condvar->wait (entry->condvar, this->entries_lock);
+ }
+ }
+
+ this->entries->remove (this->entries, (void *) seq);
+ this->entries_lock->unlock (this->entries_lock);
+
+ if (!entry->complete)
+ {
+ destroy_entry (entry);
+ DBG1 (DBG_KNL, "vac timeout");
+ return OUT_OF_RES;
+ }
+
+ for (i = 0, *out_len = 0; i < array_count (entry->rmsgs); i++)
+ {
+ array_get (entry->rmsgs, i, &rmsg);
+ *out_len += rmsg->data_len;
+ }
+ ptr = malloc (*out_len);
+ *out = ptr;
+ while (array_remove (entry->rmsgs, ARRAY_HEAD, &rmsg))
+ {
+ memcpy (ptr, rmsg->data, rmsg->data_len);
+ ptr += rmsg->data_len;
+ free (rmsg);
+ }
+
+ destroy_entry (entry);
+
+ return SUCCESS;
+}
+
+METHOD (vac_t, vac_send, status_t, private_vac_t *this, char *in, int in_len,
+ char **out, int *out_len)
+{
+ return send_vac (this, in, in_len, out, out_len, FALSE);
+}
+
+METHOD (vac_t, vac_send_dump, status_t, private_vac_t *this, char *in,
+ int in_len, char **out, int *out_len)
+{
+ return send_vac (this, in, in_len, out, out_len, TRUE);
+}
+
+METHOD (vac_t, register_event, status_t, private_vac_t *this, char *in,
+ int in_len, event_cb_t cb, uint16_t event_id, void *ctx)
+{
+ char *out;
+ int out_len;
+ want_event_reply_t *rmp;
+ uintptr_t id = (uintptr_t) event_id;
+ event_t *event;
+
+ if (vac->send (vac, in, in_len, &out, &out_len))
+ return FAILED;
+ rmp = (void *) out;
+ if (rmp->retval)
+ return FAILED;
+ free (out);
+ vl_msg_api_free (in);
+ this->events_lock->lock (this->events_lock);
+ INIT (event, .cb = cb, .ctx = ctx, );
+ this->events->put (this->events, (void *) id, event);
+ this->events_lock->unlock (this->events_lock);
+
+ return SUCCESS;
+}
+
+vac_t *
+vac_create (char *name)
+{
+ private_vac_t *this;
+
+ INIT(this,
+ .public = {
+ .destroy = _destroy,
+ .send = _vac_send,
+ .send_dump = _vac_send_dump,
+ .register_event = _register_event,
+ },
+ .rx_is_running = FALSE,
+ .read_timeout = lib->settings->get_int(lib->settings,
+ "%s.plugins.kernel-vpp.read_timeout", 0, lib->ns),
+ .queue_lock = mutex_create(MUTEX_TYPE_DEFAULT),
+ .suspend_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .resume_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .terminate_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .entries_lock = mutex_create(MUTEX_TYPE_RECURSIVE),
+ .entries = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
+ .events_lock = mutex_create(MUTEX_TYPE_DEFAULT),
+ .events = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
+ .seq = 0,
+ );
+
+ clib_mem_init_thread_safe (0, 256 << 20);
+
+ if (vl_client_api_map ("/vpe-api"))
+ {
+ DBG1 (DBG_KNL, "vac unable to map");
+ destroy (this);
+ return NULL;
+ }
+
+ if (vl_client_connect (name, 0, 32) < 0)
+ {
+ DBG1 (DBG_KNL, "vac unable to connect");
+ vl_client_api_unmap ();
+ destroy (this);
+ return NULL;
+ }
+
+ this->connected_to_vlib = TRUE;
+
+ this->rx = thread_create ((thread_main_t) vac_rx_thread_fn, this);
+ if (!this->rx)
+ {
+ vl_client_api_unmap ();
+ destroy (this);
+ return NULL;
+ }
+ this->rx_is_running = TRUE;
+
+ vac = &this->public;
+ return &this->public;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_shared.h b/extras/strongswan/vpp_sswan/kernel_vpp_shared.h
new file mode 100644
index 00000000000..7e8d2035c56
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_shared.h
@@ -0,0 +1,93 @@
+#ifndef KERNEL_VPP_SHARED_H_
+#define KERNEL_VPP_SHARED_H_
+/*
+ * Copyright (c) 2022 Intel 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.
+ */
+
+/**
+ * Every 2 seconds, the thread responsible for collecting the available
+ * interfaces will be executed.
+ * Retrying 5 times every 1 second ensures that there is enough time to check
+ * if the interface will be available.
+ */
+#define N_RETRY_GET_IF 5
+
+typedef struct vac_t vac_t;
+
+/**
+ * Callback function invoked for received event messages.
+ *
+ * @param data associated event message, destroyed by VPP API wrapper
+ * @param data_len length of the event message
+ * @param ctx user data, as passed to register_event
+ */
+typedef void (*event_cb_t) (char *data, int data_len, void *ctx);
+
+/**
+ * Wrapper around VPP binary API client.
+ */
+struct vac_t
+{
+
+ /**
+ * Destroy the VPP API client.
+ */
+ void (*destroy) (vac_t *this);
+
+ /**
+ * Send VPP API message and wait for a reply
+ *
+ * @param in VPP API message to send
+ * @param in_len length of the message to send
+ * @param out received VPP API message
+ * @param out_len length of the received message
+ */
+ status_t (*send) (vac_t *this, char *in, int in_len, char **out,
+ int *out_len);
+
+ /**
+ * Send VPP API dump message and wait for a reply.
+ *
+ * @param in VPP API message to send
+ * @param in_len length of the message to send
+ * @param out received VPP API message
+ * @param out_len length of the received message
+ */
+ status_t (*send_dump) (vac_t *this, char *in, int in_len, char **out,
+ int *out_len);
+
+ /**
+ * Register for VPP API event of a given kind.
+ *
+ * @param in VPP API event message to register
+ * @param in_len length of the event message to register
+ * @param cb callback function to register
+ * @param event_id event ID
+ * @param ctx user data passed to callback invocations
+ */
+ status_t (*register_event) (vac_t *this, char *in, int in_len, event_cb_t cb,
+ uint16_t event_id, void *ctx);
+};
+
+extern vac_t *vac;
+
+/**
+ * Establishing a binary API connection to VPP.
+ *
+ * @param name client name
+ * @return vac_t instance
+ */
+vac_t *vac_create (char *name);
+
+#endif /* KERNEL_VPP_SHARED_H_ */
diff --git a/extras/strongswan/vpp_sswan/swanctl.conf b/extras/strongswan/vpp_sswan/swanctl.conf
new file mode 100644
index 00000000000..f3e7a78101f
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/swanctl.conf
@@ -0,0 +1,35 @@
+connections {
+ net-net {
+ local_addrs = 192.168.0.2
+ remote_addrs = 192.168.0.1
+ local {
+ auth = psk
+ id = sun.strongswan.org
+ }
+ remote {
+ auth = psk
+ id = moon.strongswan.org
+ }
+ children {
+ net-net {
+ local_ts = 192.168.200.0/24
+ remote_ts = 192.168.100.0/24
+ esp_proposals = aes128-sha1-modp2048
+ rekey_time = 240m
+ }
+ }
+ version = 2
+ mobike = yes
+ encap = no # NAT-T if needed
+ proposals = aes128-sha256-x25519
+ }
+}
+secrets {
+ ike-net-net {
+ id = moon.strongswan.org
+ secret = simplepsk
+ }
+}
+
+# Include config snippets
+include conf.d/*.conf
diff --git a/extras/vagrant/build.sh b/extras/vagrant/build.sh
index ea32ffe90c3..631b9909d3e 100755
--- a/extras/vagrant/build.sh
+++ b/extras/vagrant/build.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Get Command Line arguements if present
VPP_DIR=$1
@@ -32,7 +32,7 @@ echo KERNEL_MACHINE: $KERNEL_MACHINE
echo KERNEL_RELEASE: $KERNEL_RELEASE
echo KERNEL_VERSION: $KERNEL_VERSION
echo OS_ID: $OS_ID
-echo OS_VERSION_ID: $OS_ID
+echo OS_VERSION_ID: $OS_VERSION_ID
# Install dependencies
cd $VPP_DIR
diff --git a/extras/vagrant/clearinterfaces.sh b/extras/vagrant/clearinterfaces.sh
index 31a63098e87..50b96f0d8b0 100755
--- a/extras/vagrant/clearinterfaces.sh
+++ b/extras/vagrant/clearinterfaces.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Capture all the interface IPs, in case we need them later
ip -o addr show > ~vagrant/ifconfiga
diff --git a/extras/vagrant/install.sh b/extras/vagrant/install.sh
index cfe5fe5b2d0..67a2e4fb990 100755
--- a/extras/vagrant/install.sh
+++ b/extras/vagrant/install.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Get Command Line arguements if present
VPP_DIR=$1
diff --git a/extras/vagrant/run.sh b/extras/vagrant/run.sh
index 3e87e259442..be2d8425903 100755
--- a/extras/vagrant/run.sh
+++ b/extras/vagrant/run.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Figure out what system we are running on
if [ "$(uname)" <> "Darwin" ] ; then
diff --git a/extras/vagrant/update.sh b/extras/vagrant/update.sh
index 3fb456b26eb..e56e6ba004e 100755
--- a/extras/vagrant/update.sh
+++ b/extras/vagrant/update.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Make sure that we get the hugepages we need on provision boot
# Note: The package install should take care of this at the end
diff --git a/extras/vagrant/vcl_test.sh b/extras/vagrant/vcl_test.sh
index 5d58d73745f..2bad0f6e14f 100755
--- a/extras/vagrant/vcl_test.sh
+++ b/extras/vagrant/vcl_test.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
if [ -n "$1" ]; then
VPP_DIR=$1
diff --git a/extras/vcl-ldpreload/README.md b/extras/vcl-ldpreload/README.md
deleted file mode 100644
index 81c86f648c8..00000000000
--- a/extras/vcl-ldpreload/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# vcl-ldpreload: a LD_PRELOAD library that uses the VPP Communications Library (VCL). {#vcl_ldpreload_doc}
-
-User can LD_PRELOAD any application that uses POSIX socket API.
-
-NOTE: The sources have been moved to .../vpp/src/vcl and
- libvcl_ldpreload.so is built with VPP and can be found in
- .../vpp/build-root/install-vpp[_debug]-native/vpp/lib
-
-## HowTo
-
-# 1. Running the demo
-## Run test script without parameters to see help menu:
-
-export WS_ROOT=<top level vpp git repo dir> (e.g. /scratch/my_name/vpp)
-$WS_ROOT/test/scripts/socket_test.sh
-
-# 2. Docker iPerf examples.
-## These launch xterms. To quit, close xterms and run following docker kill cmd (WARNING: This will kill all docker containers!) 'docker kill $(docker ps -q)'
-
-
-## Docker iPerf using default Linux Bridge
-
-$WS_ROOT/test/scripts/socket_test.sh -bi docker-kernel
-
-## Docker iPerf using VPP
-$WS_ROOT/test/scripts/socket_test.sh -bi docker-preload
-
diff --git a/extras/vcl-ldpreload/README.rst b/extras/vcl-ldpreload/README.rst
new file mode 100644
index 00000000000..7168697ec01
--- /dev/null
+++ b/extras/vcl-ldpreload/README.rst
@@ -0,0 +1,40 @@
+.. _vcl_ldpreload_doc:
+
+LD_PRELOAD the VCL
+==================
+
+vcl-ldpreload is a LD_PRELOAD library that uses the VPP Communications Library (VCL).
+
+User can LD_PRELOAD any application that uses POSIX socket API.
+
+NOTE: The sources have been moved to ``vpp/src/vcl`` and ``libvcl_ldpreload.so`` is built with VPP and can be found in
+``vpp/build-root/install-vpp[_debug]-native/vpp/lib``
+
+1. Running the demo
+-------------------
+
+Run test script without parameters to see help menu:
+
+::
+
+ export WS_ROOT= (e.g. /scratch/my_name/vpp)
+ $WS_ROOT/test/scripts/socket_test.sh
+
+
+2. Docker iPerf examples
+------------------------
+
+These launch xterms. To quit, close xterms and run following docker kill cmd (WARNING: This will kill all docker containers!) ‘docker kill $(docker ps -q)’
+
+Docker iPerf using default Linux Bridge
+
+::
+
+ $WS_ROOT/test/scripts/socket_test.sh -bi docker-kernel
+
+
+Docker iPerf using VPP
+
+::
+
+ $WS_ROOT/test/scripts/socket_test.sh -bi docker-preload
diff --git a/extras/vpp_config/README.rst b/extras/vpp_config/README.rst
index 8995edfce9f..19fc9166dad 100644
--- a/extras/vpp_config/README.rst
+++ b/extras/vpp_config/README.rst
@@ -1,514 +1,517 @@
-Summary:
-
-The purpose of the VPP configuration utility is to allow the user to configure
-VPP in a simple and safe manner. The utility takes input from the user and
-then modifies the key configuration files. The user can then examine these files
-to be sure they are correct and then actually apply the configuration. The user
-can also install a released and stable version of VPP. This is currently
-released with release 17.10.
-
-Use:
-
-The installation and executing of the VPP configuration utility is simple. First
-install the python pip module. Using pip install, then pip install vpp-config.
-Then simply type �vpp-config� and answer the questions. If you are not sure what
-to answer choose the default. For yes or no questions the capital letter
-designates the default. For example, for a question that shows [Y/n] Y is the
-default. For numbers the default is within the brackets for example for a
-question that shows [1024]. 1024 is the default.
-
-The flow of the utility is to inspect the system, if VPP is not install it,
-create dry run configurations, inspect the files created during the dry run,
-apply the configuration and then inspect the system again and then repeat.
-
-Caveats:
-
-- Supports Ubuntu, centos7, RedHat is coming shortly.
-
-For Developers:
-
-Modifying the code is reasonable simple. The process would be edit and debug the
-code from the root directory. In order to do this, we need a script that will copy
-or data files to the proper place. This is where they end up with pip install. For
-Ubuntu, this is /usr/local/vpp/vpp-config. I have provided a script that will copy
-the relevant files correctly. I have also provided a script that will clean the
-environment so you can start from scratch. These are the steps to run the utility
-in this environment. The scripts are meant to be run from the root directory.
-
- ./scripts/clean.sh
- ./scripts/cp-data.sh
- ./vpp-config
-
-When the utility is installed with pip the wrapper scripts/vpp-config is written to
-/usr/local/bin. However, the starting point when debugging this script locally is
-vpp-config. Run the utility by executing vpp-config.
-
-The start point in the code is in vpp_config.py. However, most of the work is
-done in
-the files in ./vpplib
-
-Uploading to PyPi:
-
-To upload this utility to PpPi simple do the following. Currently, I have my own account
-when we want everyone to contribute we will need to change that.
-
- sudo �H bash
- cd vpp_config
- python setup.py sdist bdist_wheel
- twine upload dist/*
-
-Example Run:
-
-# pip install vpp-config
-# vpp-config
-
-Welcome to the VPP system configuration utility
-
-These are the files we will modify:
- /etc/vpp/startup.conf
- /etc/sysctl.d/80-vpp.conf
- /etc/default/grub
-
-Before we change them, we'll create working copies in /usr/local/vpp/vpp-config/dryrun
-Please inspect them carefully before applying the actual configuration (option 3)!
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 1
-
-==============================
-NODE: DUT1
-
-CPU:
- Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
- CPU(s): 32
- Thread(s) per core: 2
- Core(s) per socket: 8
- Socket(s): 2
- NUMA node0 CPU(s): 0-7,16-23
- NUMA node1 CPU(s): 8-15,24-31
- CPU max MHz: 3600.0000
- CPU min MHz: 1200.0000
- SMT: Enabled
-
-VPP Threads: (Name: Cpu Number)
-
-Grub Command Line:
- Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
- Configured: GRUB_CMDLINE_LINUX_DEFAULT=""
-
-Huge Pages:
- Total System Memory : 65863484 kB
- Total Free Memory : 41325924 kB
- Actual Huge Page Total : 8192
- Configured Huge Page Total : 1024
- Huge Pages Free : 8192
- Huge Page Size : 2048 kB
-
-Devices:
-
-Status:
- Not Installed
-
-==============================
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 4
-
-There are no VPP packages on node localhost.
-Do you want to install VPP [Y/n]?
-INFO:root: Local Command: ls /etc/apt/sources.list.d/99fd.io.list.orig
-INFO:root: /etc/apt/sources.list.d/99fd.io.list.orig
-��..
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 1
-
-==============================
-NODE: DUT1
-
-CPU:
- Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
- CPU(s): 32
- Thread(s) per core: 2
- Core(s) per socket: 8
- Socket(s): 2
- NUMA node0 CPU(s): 0-7,16-23
- NUMA node1 CPU(s): 8-15,24-31
- CPU max MHz: 3600.0000
- CPU min MHz: 1200.0000
- SMT: Enabled
-
-VPP Threads: (Name: Cpu Number)
- vpp_main : 0
- vpp_stats : 0
-
-Grub Command Line:
- Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
- Configured: GRUB_CMDLINE_LINUX_DEFAULT=""
-
-Huge Pages:
- Total System Memory : 65863484 kB
- Total Free Memory : 55877364 kB
- Actual Huge Page Total : 1024
- Configured Huge Page Total : 1024
- Huge Pages Free : 1024
- Huge Page Size : 2048 kB
-
-Devices:
-Name Socket RXQs RXDescs TXQs TXDescs
-
-Status:
- active (running)
-
-==============================
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 2
-
-These device(s) are currently NOT being used by VPP or the OS.
-
-PCI ID Description
-----------------------------------------------------------------
-0000:02:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:02:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-
-Would you like to give any of these devices back to the OS [y/N]? y
-Would you like to use device 0000:02:00.0 for the OS [y/N]? y
-Would you like to use device 0000:02:00.1 for the OS [y/N]? y
-
-These devices have kernel interfaces, but appear to be safe to use with VPP.
-
-PCI ID Kernel Interface(s) Description
-------------------------------------------------------------------------------------------
-0000:90:00.0 enp144s0 VIC Ethernet NIC
-0000:8f:00.0 enp143s0 VIC Ethernet NIC
-0000:84:00.0 enp132s0f0,enp132s0f0d1 Ethernet Controller XL710 for 40GbE QSFP+
-0000:84:00.1 enp132s0f1,enp132s0f1d1 Ethernet Controller XL710 for 40GbE QSFP+
-0000:08:00.1 enp8s0f1 I350 Gigabit Network Connection
-0000:02:00.0 enp2s0f0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:02:00.1 enp2s0f1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:86:00.0 enp134s0f0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:86:00.1 enp134s0f1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-
-Would you like to use any of these device(s) for VPP [y/N]? y
-Would you like to use device 0000:90:00.0 for VPP [y/N]?
-Would you like to use device 0000:8f:00.0 for VPP [y/N]?
-Would you like to use device 0000:84:00.0 for VPP [y/N]?
-Would you like to use device 0000:84:00.1 for VPP [y/N]?
-Would you like to use device 0000:08:00.1 for VPP [y/N]?
-Would you like to use device 0000:02:00.0 for VPP [y/N]? y
-Would you like to use device 0000:02:00.1 for VPP [y/N]? y
-Would you like to use device 0000:86:00.0 for VPP [y/N]? y
-Would you like to use device 0000:86:00.1 for VPP [y/N]? y
-
-These device(s) will be used by VPP.
-
-PCI ID Description
-----------------------------------------------------------------
-0000:86:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:86:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:02:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:02:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-
-Would you like to remove any of these device(s) [y/N]?
-
-These device(s) will be used by VPP, please rerun this option if this is incorrect.
-
-PCI ID Description
-----------------------------------------------------------------
-0000:86:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:86:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:02:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
-0000:02:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
-
-Your system has 32 core(s) and 2 Numa Nodes.
-To begin, we suggest not reserving any cores for VPP or other processes.
-Then to improve performance try reserving cores as needed.
-
-How many core(s) do you want to reserve for processes other than VPP? [0-16][0]? 4
-How many core(s) shall we reserve for VPP workers[0-4][0]? 2
-Should we reserve 1 core for the VPP Main thread? [Y/n]?
-
-There currently 1024 2048 kB huge pages free.
-Do you want to reconfigure the number of huge pages [y/N]? y
-
-There currently a total of 1024 huge pages.
-How many huge pages do you want [1024 - 22511][1024]? 8192
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 3
-
-We are now going to configure your system(s).
-
-Are you sure you want to do this [Y/n]?
-These are the changes we will apply to
-the huge page file (/etc/sysctl.d/80-vpp.conf).
-
-1,2d0
-< # Number of 2MB hugepages desired
-< vm.nr_hugepages=1024
-4,7c2,3
-< # Must be greater than or equal to (2 * vm.nr_hugepages).
-< vm.max_map_count=3096
-<
-< # All groups allowed to access hugepages
----
-> vm.nr_hugepages=8192
-> vm.max_map_count=17408
-8a5
-> kernel.shmmax=17179869184
-10,15d6
-< # Shared Memory Max must be greator or equal to the total size of hugepages.
-< # For 2MB pages, TotalHugepageSize = vm.nr_hugepages * 2 * 1024 * 1024
-< # If the existing kernel.shmmax setting (cat /sys/proc/kernel/shmmax)
-< # is greater than the calculated TotalHugepageSize then set this parameter
-< # to current shmmax value.
-< kernel.shmmax=2147483648
-
-
-Are you sure you want to apply these changes [Y/n]?
-These are the changes we will apply to
-the VPP startup file (/etc/vpp/startup.conf).
-
-3c3
-< nodaemon
----
-> interactive
-5a6
-> cli-listen /run/vpp/cli.sock
-17c18,25
-< ## In the VPP there is one main thread and optionally the user can create worker(s)
----
->
-> main-core 8
-> corelist-workers 9-10,5-6
->
-> scheduler-policy fifo
-> scheduler-priority 50
->
-> ## In the VPP there is one main thread and optionally the user can create worker(s)
-52,53c60,76
-< # dpdk {
-< ## Change default settings for all intefaces
----
-> dpdk {
->
-> dev 0000:86:00.0 {
-> num-rx-queues 2
-> }
-> dev 0000:86:00.1 {
-> num-rx-queues 2
-> }
-> dev 0000:02:00.0 {
-> num-rx-queues 2
-> }
-> dev 0000:02:00.1 {
-> num-rx-queues 2
-> }
-> num-mbufs 71680
->
-> ## Change default settings for all intefaces
-82a106,115
-> ## Specify bonded interface and its slaves via PCI addresses
-> ##
-> ## Bonded interface in XOR load balance mode (mode 2) with L3 and L4 headers
-> # vdev eth_bond0,mode=2,slave=0000:02:00.0,slave=0000:03:00.0,xmit_policy=l34
-> # vdev eth_bond1,mode=2,slave=0000:02:00.1,slave=0000:03:00.1,xmit_policy=l34
-> ##
-> ## Bonded interface in Active-Back up mode (mode 1)
-> # vdev eth_bond0,mode=1,slave=0000:02:00.0,slave=0000:03:00.0
-> # vdev eth_bond1,mode=1,slave=0000:02:00.1,slave=0000:03:00.1
->
-99c132
-< # }
----
-> }
-108a142
->
-
-
-Are you sure you want to apply these changes [Y/n]?
-
-The configured grub cmdline looks like this:
-GRUB_CMDLINE_LINUX_DEFAULT="intel_pstate=disable isolcpus=1-4,8,9-10,5-6 nohz_full=1-4,8,9-10,5-6 rcu_nocbs=1-4,8,9-10,5-6"
-
-The current boot cmdline looks like this:
-BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
-
-Do you want to keep the current boot cmdline [Y/n]?
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 1
-
-==============================
-NODE: DUT1
-
-CPU:
- Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
- CPU(s): 32
- Thread(s) per core: 2
- Core(s) per socket: 8
- Socket(s): 2
- NUMA node0 CPU(s): 0-7,16-23
- NUMA node1 CPU(s): 8-15,24-31
- CPU max MHz: 3600.0000
- CPU min MHz: 1200.0000
- SMT: Enabled
-
-VPP Threads: (Name: Cpu Number)
-
-Grub Command Line:
- Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
- Configured: GRUB_CMDLINE_LINUX_DEFAULT="intel_pstate=disable isolcpus=1-4,8,9-10,5-6 nohz_full=1-4,8,9-10,5-6 rcu_nocbs=1-4,8,9-10,5-6"
-
-Huge Pages:
- Total System Memory : 65863484 kB
- Total Free Memory : 41163916 kB
- Actual Huge Page Total : 8192
- Configured Huge Page Total : 8192
- Huge Pages Free : 3108
- Huge Page Size : 2048 kB
-
-Devices:
-Total Number of Buffers: 71680
-
-Status:
- active (running)
- Sep 27 12:49:59 tf-ucs-3 vpp[13671]: EAL: No free hugepages reported in hugepages-1048576kB
-
-==============================
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: 1
-
-==============================
-NODE: DUT1
-
-CPU:
- Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
- CPU(s): 32
- Thread(s) per core: 2
- Core(s) per socket: 8
- Socket(s): 2
- NUMA node0 CPU(s): 0-7,16-23
- NUMA node1 CPU(s): 8-15,24-31
- CPU max MHz: 3600.0000
- CPU min MHz: 1200.0000
- SMT: Enabled
-
-VPP Threads: (Name: Cpu Number)
- vpp_stats : 0
- vpp_wk_2 : 9
- vpp_wk_3 : 10
- vpp_wk_0 : 5
- vpp_wk_1 : 6
- vpp_main : 8
-
-Grub Command Line:
- Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
- Configured: GRUB_CMDLINE_LINUX_DEFAULT="intel_pstate=disable isolcpus=1-4,8,9-10,5-6 nohz_full=1-4,8,9-10,5-6 rcu_nocbs=1-4,8,9-10,5-6"
-
-Huge Pages:
- Total System Memory : 65863484 kB
- Total Free Memory : 41170684 kB
- Actual Huge Page Total : 8192
- Configured Huge Page Total : 8192
- Huge Pages Free : 7936
- Huge Page Size : 2048 kB
-
-Devices:
-Total Number of Buffers: 71680
-Name Socket RXQs RXDescs TXQs TXDescs
-TenGigabitEthernet2/0/0 0 2 1024 5 1024
-TenGigabitEthernet2/0/1 0 2 1024 5 1024
-TenGigabitEthernet86/0/0 1 2 1024 5 1024
-TenGigabitEthernet86/0/1 1 2 1024 5 1024
-
-Status:
- active (running)
-
-==============================
-
-What would you like to do?
-
-1) Show basic system information
-2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
- and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
-3) Full configuration (WARNING: This will change the system configuration)
-4) Install/Uninstall VPP.
-5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
-6) Install QEMU patch (Needed when running openstack).
-9 or q) Quit
-
-Command: q
-#
+VPP configuration utility
+=========================
+
+The purpose of the VPP configuration utility is to allow the user to configure
+VPP in a simple and safe manner. The utility takes input from the user and
+then modifies the key configuration files. The user can then examine these files
+to be sure they are correct and then actually apply the configuration. The user
+can also install a released and stable version of VPP. This is currently
+released with release 17.10.
+
+VPP config Usage
+----------------
+
+The installation and executing of the VPP configuration utility is simple. First
+install the python pip module. Using pip install, then pip install vpp-config.
+Then simply type ``vpp-config`` and answer the questions. If you are not sure what
+to answer choose the default. For yes or no questions the capital letter
+designates the default. For example, for a question that shows [Y/n] Y is the
+default. For numbers the default is within the brackets for example for a
+question that shows [1024]. 1024 is the default.
+
+The flow of the utility is to inspect the system, if VPP is not install it,
+create dry run configurations, inspect the files created during the dry run,
+apply the configuration and then inspect the system again and then repeat.
+
+Caveats
+-------
+
+- Supports Ubuntu, centos7, RedHat is coming shortly.
+
+For Developers:
+
+Modifying the code is reasonable simple. The process would be edit and debug the
+code from the root directory. In order to do this, we need a script that will copy
+or data files to the proper place. This is where they end up with pip install. For
+Ubuntu, this is ``/usr/local/vpp/vpp-config``. I have provided a script that will copy
+the relevant files correctly. I have also provided a script that will clean the
+environment so you can start from scratch. These are the steps to run the utility
+in this environment. The scripts are meant to be run from the root directory.
+
+.. code-block:: console
+
+ ./scripts/clean.sh
+ ./scripts/cp-data.sh
+ ./vpp-config
+
+When the utility is installed with pip the wrapper ``scripts/vpp-config`` is written to
+``/usr/local/bin``. However, the starting point when debugging this script locally is
+vpp-config. Run the utility by executing vpp-config.
+
+The start point in the code is in vpp_config.py. However, most of the work is
+done in the files in ``./vpplib``
+
+Uploading to PyPi
+-----------------
+
+To upload this utility to PpPi simple do the following. Currently, I have my own account
+when we want everyone to contribute we will need to change that.
+
+
+.. code-block:: console
+
+ sudo bash
+ cd vpp_config
+ python setup.py sdist bdist_wheel
+ twine upload dist/*
+
+Example Run:
+
+.. code-block:: console
+
+ # pip install vpp-config
+ # vpp-config
+
+ Welcome to the VPP system configuration utility
+
+ These are the files we will modify:
+
+ /etc/vpp/startup.conf
+ /etc/sysctl.d/80-vpp.conf
+ /etc/default/grub
+
+ Before we change them, we'll create working copies in ``/usr/local/vpp/vpp-config/dryrun``
+ Please inspect them carefully before applying the actual configuration (option 3)!
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 1
+
+ NODE: DUT1
+
+ CPU:
+ Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
+ CPU(s): 32
+ Thread(s) per core: 2
+ Core(s) per socket: 8
+ Socket(s): 2
+ NUMA node0 CPU(s): 0-7,16-23
+ NUMA node1 CPU(s): 8-15,24-31
+ CPU max MHz: 3600.0000
+ CPU min MHz: 1200.0000
+ SMT: Enabled
+
+ VPP Threads: (Name: Cpu Number)
+
+ Grub Command Line:
+ Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
+ Configured: GRUB_CMDLINE_LINUX_DEFAULT=""
+
+ Huge Pages:
+ Total System Memory : 65863484 kB
+ Total Free Memory : 41325924 kB
+ Actual Huge Page Total : 8192
+ Configured Huge Page Total : 1024
+ Huge Pages Free : 8192
+ Huge Page Size : 2048 kB
+
+ Devices:
+
+ Status:
+ Not Installed
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 4
+
+ There are no VPP packages on node localhost.
+ Do you want to install VPP [Y/n]?
+ INFO:root: Local Command: ls /etc/apt/sources.list.d/99fd.io.list.orig
+ INFO:root: /etc/apt/sources.list.d/99fd.io.list.orig
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 1
+
+ ==============================
+ NODE: DUT1
+
+ CPU:
+ Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
+ CPU(s): 32
+ Thread(s) per core: 2
+ Core(s) per socket: 8
+ Socket(s): 2
+ NUMA node0 CPU(s): 0-7,16-23
+ NUMA node1 CPU(s): 8-15,24-31
+ CPU max MHz: 3600.0000
+ CPU min MHz: 1200.0000
+ SMT: Enabled
+
+ VPP Threads: (Name: Cpu Number)
+ vpp_main : 0
+ vpp_stats : 0
+
+ Grub Command Line:
+ Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
+ Configured: GRUB_CMDLINE_LINUX_DEFAULT=""
+
+ Huge Pages:
+ Total System Memory : 65863484 kB
+ Total Free Memory : 55877364 kB
+ Actual Huge Page Total : 1024
+ Configured Huge Page Total : 1024
+ Huge Pages Free : 1024
+ Huge Page Size : 2048 kB
+
+ Devices:
+ Name Socket RXQs RXDescs TXQs TXDescs
+
+ Status:
+ active (running)
+
+ ==============================
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 2
+
+ These device(s) are currently NOT being used by VPP or the OS.
+
+ PCI ID Description
+ ----------------------------------------------------------------
+ 0000:02:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:02:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+
+ Would you like to give any of these devices back to the OS [y/N]? y
+ Would you like to use device 0000:02:00.0 for the OS [y/N]? y
+ Would you like to use device 0000:02:00.1 for the OS [y/N]? y
+
+ These devices have kernel interfaces, but appear to be safe to use with VPP.
+
+ PCI ID Kernel Interface(s) Description
+ ------------------------------------------------------------------------------------------
+ 0000:90:00.0 enp144s0 VIC Ethernet NIC
+ 0000:8f:00.0 enp143s0 VIC Ethernet NIC
+ 0000:84:00.0 enp132s0f0,enp132s0f0d1 Ethernet Controller XL710 for 40GbE QSFP+
+ 0000:84:00.1 enp132s0f1,enp132s0f1d1 Ethernet Controller XL710 for 40GbE QSFP+
+ 0000:08:00.1 enp8s0f1 I350 Gigabit Network Connection
+ 0000:02:00.0 enp2s0f0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:02:00.1 enp2s0f1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:86:00.0 enp134s0f0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:86:00.1 enp134s0f1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+
+ Would you like to use any of these device(s) for VPP [y/N]? y
+ Would you like to use device 0000:90:00.0 for VPP [y/N]?
+ Would you like to use device 0000:8f:00.0 for VPP [y/N]?
+ Would you like to use device 0000:84:00.0 for VPP [y/N]?
+ Would you like to use device 0000:84:00.1 for VPP [y/N]?
+ Would you like to use device 0000:08:00.1 for VPP [y/N]?
+ Would you like to use device 0000:02:00.0 for VPP [y/N]? y
+ Would you like to use device 0000:02:00.1 for VPP [y/N]? y
+ Would you like to use device 0000:86:00.0 for VPP [y/N]? y
+ Would you like to use device 0000:86:00.1 for VPP [y/N]? y
+
+ PCI ID Description
+ ----------------------------------------------------------------
+ 0000:86:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:86:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:02:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:02:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+
+ Would you like to remove any of these device(s) [y/N]?
+
+ These device(s) will be used by VPP, please rerun this option if this is incorrect.
+
+ PCI ID Description
+ ----------------------------------------------------------------
+ 0000:86:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:86:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:02:00.0 82599ES 10-Gigabit SFI/SFP+ Network Connection
+ 0000:02:00.1 82599ES 10-Gigabit SFI/SFP+ Network Connection
+
+ Your system has 32 core(s) and 2 Numa Nodes.
+ To begin, we suggest not reserving any cores for VPP or other processes.
+ Then to improve performance try reserving cores as needed.
+
+ How many core(s) do you want to reserve for processes other than VPP? [0-16][0]? 4
+ How many core(s) shall we reserve for VPP workers[0-4][0]? 2
+ Should we reserve 1 core for the VPP Main thread? [Y/n]?
+
+ There currently 1024 2048 kB huge pages free.
+ Do you want to reconfigure the number of huge pages [y/N]? y
+
+ There currently a total of 1024 huge pages.
+ How many huge pages do you want [1024 - 22511][1024]? 8192
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 3
+
+ We are now going to configure your system(s).
+
+ Are you sure you want to do this [Y/n]?
+ These are the changes we will apply to
+ the huge page file (/etc/sysctl.d/80-vpp.conf).
+
+ 1,2d0
+ < # Number of 2MB hugepages desired
+ < vm.nr_hugepages=1024
+ 4,7c2,3
+ < # Must be greater than or equal to (2 * vm.nr_hugepages).
+ < vm.max_map_count=3096
+ <
+ < # All groups allowed to access hugepages
+ ---
+ > vm.nr_hugepages=8192
+ > vm.max_map_count=17408
+ 8a5
+ > kernel.shmmax=17179869184
+ 10,15d6
+ < # Shared Memory Max must be greator or equal to the total size of hugepages.
+ < # For 2MB pages, TotalHugepageSize = vm.nr_hugepages * 2 * 1024 * 1024
+ < # If the existing kernel.shmmax setting (cat /sys/proc/kernel/shmmax)
+ < # is greater than the calculated TotalHugepageSize then set this parameter
+ < # to current shmmax value.
+ < kernel.shmmax=2147483648
+
+ Are you sure you want to apply these changes [Y/n]?
+ These are the changes we will apply to
+ the VPP startup file (/etc/vpp/startup.conf).
+
+ 3c3
+ < nodaemon
+ ---
+ > interactive
+ 5a6
+ > cli-listen /run/vpp/cli.sock
+ 17c18,25
+ < ## In the VPP there is one main thread and optionally the user can create worker(s)
+ ---
+ >
+ > main-core 8
+ > corelist-workers 9-10,5-6
+ >
+ > scheduler-policy fifo
+ > scheduler-priority 50
+ >
+ > ## In the VPP there is one main thread and optionally the user can create worker(s)
+ 52,53c60,76
+ < # dpdk {
+ < ## Change default settings for all intefaces
+ ---
+ > dpdk {
+ >
+ > dev 0000:86:00.0 {
+ > num-rx-queues 2
+ > }
+ > dev 0000:86:00.1 {
+ > num-rx-queues 2
+ > }
+ > dev 0000:02:00.0 {
+ > num-rx-queues 2
+ > }
+ > dev 0000:02:00.1 {
+ > num-rx-queues 2
+ > }
+ > num-mbufs 71680
+ >
+ > ## Change default settings for all intefaces
+ 82a106,115
+ > ## Specify bonded interface and its slaves via PCI addresses
+ > ##
+ > ## Bonded interface in XOR load balance mode (mode 2) with L3 and L4 headers
+ > # vdev eth_bond0,mode=2,slave=0000:02:00.0,slave=0000:03:00.0,xmit_policy=l34
+ > # vdev eth_bond1,mode=2,slave=0000:02:00.1,slave=0000:03:00.1,xmit_policy=l34
+ > ##
+ > ## Bonded interface in Active-Back up mode (mode 1)
+ > # vdev eth_bond0,mode=1,slave=0000:02:00.0,slave=0000:03:00.0
+ > # vdev eth_bond1,mode=1,slave=0000:02:00.1,slave=0000:03:00.1
+ >
+ 99c132
+ < # }
+ ---
+ > }
+ 108a142
+ >
+
+ Are you sure you want to apply these changes [Y/n]?
+
+ The configured grub cmdline looks like this:
+ GRUB_CMDLINE_LINUX_DEFAULT="intel_pstate=disable isolcpus=1-4,8,9-10,5-6 nohz_full=1-4,8,9-10,5-6 rcu_nocbs=1-4,8,9-10,5-6"
+
+ The current boot cmdline looks like this:
+ BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
+
+ Do you want to keep the current boot cmdline [Y/n]?
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 1
+
+ ==============================
+ NODE: DUT1
+
+ CPU:
+ Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
+ CPU(s): 32
+ Thread(s) per core: 2
+ Core(s) per socket: 8
+ Socket(s): 2
+ NUMA node0 CPU(s): 0-7,16-23
+ NUMA node1 CPU(s): 8-15,24-31
+ CPU max MHz: 3600.0000
+ CPU min MHz: 1200.0000
+ SMT: Enabled
+
+ VPP Threads: (Name: Cpu Number)
+
+ Grub Command Line:
+ Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
+ Configured: GRUB_CMDLINE_LINUX_DEFAULT="intel_pstate=disable isolcpus=1-4,8,9-10,5-6 nohz_full=1-4,8,9-10,5-6 rcu_nocbs=1-4,8,9-10,5-6"
+
+ Huge Pages:
+ Total System Memory : 65863484 kB
+ Total Free Memory : 41163916 kB
+ Actual Huge Page Total : 8192
+ Configured Huge Page Total : 8192
+ Huge Pages Free : 3108
+ Huge Page Size : 2048 kB
+
+ Devices:
+ Total Number of Buffers: 71680
+
+ Status:
+ active (running)
+ Sep 27 12:49:59 tf-ucs-3 vpp[13671]: EAL: No free hugepages reported in hugepages-1048576kB
+
+ ==============================
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: 1
+
+ ==============================
+ NODE: DUT1
+
+ CPU:
+ Model name: Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz
+ CPU(s): 32
+ Thread(s) per core: 2
+ Core(s) per socket: 8
+ Socket(s): 2
+ NUMA node0 CPU(s): 0-7,16-23
+ NUMA node1 CPU(s): 8-15,24-31
+ CPU max MHz: 3600.0000
+ CPU min MHz: 1200.0000
+ SMT: Enabled
+
+ VPP Threads: (Name: Cpu Number)
+ vpp_stats : 0
+ vpp_wk_2 : 9
+ vpp_wk_3 : 10
+ vpp_wk_0 : 5
+ vpp_wk_1 : 6
+ vpp_main : 8
+
+ Grub Command Line:
+ Current: BOOT_IMAGE=/boot/vmlinuz-4.4.0-96-generic root=UUID=d760b82f-f37b-47e2-9815-db8d479a3557 ro
+ Configured: GRUB_CMDLINE_LINUX_DEFAULT="intel_pstate=disable isolcpus=1-4,8,9-10,5-6 nohz_full=1-4,8,9-10,5-6 rcu_nocbs=1-4,8,9-10,5-6"
+
+ Huge Pages:
+ Total System Memory : 65863484 kB
+ Total Free Memory : 41170684 kB
+ Actual Huge Page Total : 8192
+ Configured Huge Page Total : 8192
+ Huge Pages Free : 7936
+ Huge Page Size : 2048 kB
+
+ Devices:
+ Total Number of Buffers: 71680
+ Name Socket RXQs RXDescs TXQs TXDescs
+ TenGigabitEthernet2/0/0 0 2 1024 5 1024
+ TenGigabitEthernet2/0/1 0 2 1024 5 1024
+ TenGigabitEthernet86/0/0 1 2 1024 5 1024
+ TenGigabitEthernet86/0/1 1 2 1024 5 1024
+
+ Status:
+ active (running)
+
+ ==============================
+
+ What would you like to do?
+
+ 1) Show basic system information
+ 2) Dry Run (Will save the configuration files in /usr/local/vpp/vpp-config/dryrun for inspection)
+ and user input in /usr/local/vpp/vpp-config/configs/auto-config.yaml
+ 3) Full configuration (WARNING: This will change the system configuration)
+ 4) Install/Uninstall VPP.
+ 5) Dry Run from /usr/local/vpp/vpp-config/auto-config.yaml (will not ask questions).
+ 6) Install QEMU patch (Needed when running openstack).
+ 9 or q) Quit
+
+ Command: q
+ #
diff --git a/extras/vpp_config/data/startup.conf.template b/extras/vpp_config/data/startup.conf.template
index ccd2cf34a4e..baa58f1de53 100644
--- a/extras/vpp_config/data/startup.conf.template
+++ b/extras/vpp_config/data/startup.conf.template
@@ -98,10 +98,6 @@ dpdk {{
# num-rx-desc 512
# num-tx-desc 512
- ## VLAN strip offload mode for interface
- ## Default is off
- # vlan-strip-offload on
-
## TCP Segment Offload
## Default is off
## To enable TSO, 'enable-tcp-udp-checksum' must be set
diff --git a/extras/vpp_config/scripts/dpdk-devbind.py b/extras/vpp_config/scripts/dpdk-devbind.py
index 80edb3b9eea..140e4346894 100755
--- a/extras/vpp_config/scripts/dpdk-devbind.py
+++ b/extras/vpp_config/scripts/dpdk-devbind.py
@@ -56,9 +56,10 @@ args = []
def usage():
- '''Print usage information for the program'''
+ """Print usage information for the program"""
argv0 = basename(sys.argv[0])
- print("""
+ print(
+ """
Usage:
------
@@ -115,33 +116,40 @@ To unbind 0000:01:00.0 from using any driver
To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
%(argv0)s -b ixgbe 02:00.0 02:00.1
- """ % locals()) # replace items from local variables
+ """
+ % locals()
+ ) # replace items from local variables
# This is roughly compatible with check_output function in subprocess module
# which is only available in python 2.7.
def check_output(args, stderr=None):
- '''Run a command and capture its output'''
- return subprocess.Popen(args, stdout=subprocess.PIPE,
- stderr=stderr).communicate()[0]
+ """Run a command and capture its output"""
+ return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=stderr).communicate()[
+ 0
+ ]
def find_module(mod):
- '''find the .ko file for kernel module named mod.
+ """find the .ko file for kernel module named mod.
Searches the $RTE_SDK/$RTE_TARGET directory, the kernel
modules directory and finally under the parent directory of
- the script '''
+ the script"""
# check $RTE_SDK/$RTE_TARGET directory
- if 'RTE_SDK' in os.environ and 'RTE_TARGET' in os.environ:
- path = "%s/%s/kmod/%s.ko" % (os.environ['RTE_SDK'],
- os.environ['RTE_TARGET'], mod)
+ if "RTE_SDK" in os.environ and "RTE_TARGET" in os.environ:
+ path = "%s/%s/kmod/%s.ko" % (
+ os.environ["RTE_SDK"],
+ os.environ["RTE_TARGET"],
+ mod,
+ )
if exists(path):
return path
# check using depmod
try:
- depmod_out = check_output(["modinfo", "-n", mod],
- stderr=subprocess.STDOUT).lower()
+ depmod_out = check_output(
+ ["modinfo", "-n", mod], stderr=subprocess.STDOUT
+ ).lower()
if "error" not in depmod_out:
path = depmod_out.strip()
if exists(path):
@@ -151,7 +159,7 @@ def find_module(mod):
# check for a copy based off current path
tools_dir = dirname(abspath(sys.argv[0]))
- if (tools_dir.endswith("tools")):
+ if tools_dir.endswith("tools"):
base_dir = dirname(tools_dir)
find_out = check_output(["find", base_dir, "-name", mod + ".ko"])
if len(find_out) > 0: # something matched
@@ -161,7 +169,7 @@ def find_module(mod):
def check_modules():
- '''Checks that igb_uio is loaded'''
+ """Checks that igb_uio is loaded"""
global dpdk_drivers
# list of supported modules
@@ -170,20 +178,21 @@ def check_modules():
# first check if module is loaded
try:
# Get list of sysfs modules (both built-in and dynamically loaded)
- sysfs_path = '/sys/module/'
+ sysfs_path = "/sys/module/"
# Get the list of directories in sysfs_path
- sysfs_mods = [os.path.join(sysfs_path, o) for o
- in os.listdir(sysfs_path)
- if os.path.isdir(os.path.join(sysfs_path, o))]
+ sysfs_mods = [
+ os.path.join(sysfs_path, o)
+ for o in os.listdir(sysfs_path)
+ if os.path.isdir(os.path.join(sysfs_path, o))
+ ]
# Extract the last element of '/sys/module/abc' in the array
- sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
+ sysfs_mods = [a.split("/")[-1] for a in sysfs_mods]
# special case for vfio_pci (module is named vfio-pci,
# but its .ko is named vfio_pci)
- sysfs_mods = map(lambda a:
- a if a != 'vfio_pci' else 'vfio-pci', sysfs_mods)
+ sysfs_mods = map(lambda a: a if a != "vfio_pci" else "vfio-pci", sysfs_mods)
for mod in mods:
if mod["Name"] in sysfs_mods:
@@ -204,12 +213,12 @@ def check_modules():
def has_driver(dev_id):
- '''return true if a device is assigned to a driver. False otherwise'''
+ """return true if a device is assigned to a driver. False otherwise"""
return "Driver_str" in devices[dev_id]
def get_pci_device_details(dev_id):
- '''This function gets additional details for a PCI device'''
+ """This function gets additional details for a PCI device"""
device = {}
extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
@@ -225,8 +234,7 @@ def get_pci_device_details(dev_id):
device["Interface"] = ""
for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
if "net" in dirs:
- device["Interface"] = \
- ",".join(os.listdir(os.path.join(base, "net")))
+ device["Interface"] = ",".join(os.listdir(os.path.join(base, "net")))
break
# check if a port is used for ssh connection
device["Ssh_if"] = False
@@ -236,9 +244,9 @@ def get_pci_device_details(dev_id):
def get_nic_details():
- '''This function populates the "devices" dictionary. The keys used are
+ """This function populates the "devices" dictionary. The keys used are
the pci addresses (domain:bus:slot.func). The values are themselves
- dictionaries - one for each NIC.'''
+ dictionaries - one for each NIC."""
global devices
global dpdk_drivers
@@ -249,7 +257,7 @@ def get_nic_details():
dev = {}
dev_lines = check_output(["lspci", "-Dvmmn"]).splitlines()
for dev_line in dev_lines:
- if (len(dev_line) == 0):
+ if len(dev_line) == 0:
if dev["Class"][0:2] == NETWORK_BASE_CLASS:
# convert device and vendor ids to numbers, then add to global
dev["Vendor"] = int(dev["Vendor"], 16)
@@ -265,12 +273,13 @@ def get_nic_details():
ssh_if = []
route = check_output(["ip", "-o", "route"])
# filter out all lines for 169.254 routes
- route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
- route.decode().splitlines()))
+ route = "\n".join(
+ filter(lambda ln: not ln.startswith("169.254"), route.decode().splitlines())
+ )
rt_info = route.split()
for i in range(len(rt_info) - 1):
if rt_info[i] == "dev":
- ssh_if.append(rt_info[i+1])
+ ssh_if.append(rt_info[i + 1])
# based on the basic info, get extended text details
for d in devices.keys():
@@ -288,8 +297,7 @@ def get_nic_details():
if "Module_str" in devices[d]:
for driver in dpdk_drivers:
if driver not in devices[d]["Module_str"]:
- devices[d]["Module_str"] = \
- devices[d]["Module_str"] + ",%s" % driver
+ devices[d]["Module_str"] = devices[d]["Module_str"] + ",%s" % driver
else:
devices[d]["Module_str"] = ",".join(dpdk_drivers)
@@ -302,9 +310,9 @@ def get_nic_details():
def get_crypto_details():
- '''This function populates the "devices" dictionary. The keys used are
+ """This function populates the "devices" dictionary. The keys used are
the pci addresses (domain:bus:slot.func). The values are themselves
- dictionaries - one for each NIC.'''
+ dictionaries - one for each NIC."""
global devices
global dpdk_drivers
@@ -315,8 +323,8 @@ def get_crypto_details():
dev = {}
dev_lines = check_output(["lspci", "-Dvmmn"]).splitlines()
for dev_line in dev_lines:
- if (len(dev_line) == 0):
- if (dev["Class"][0:2] == CRYPTO_BASE_CLASS):
+ if len(dev_line) == 0:
+ if dev["Class"][0:2] == CRYPTO_BASE_CLASS:
# convert device and vendor ids to numbers, then add to global
dev["Vendor"] = int(dev["Vendor"], 16)
dev["Device"] = int(dev["Device"], 16)
@@ -336,8 +344,7 @@ def get_crypto_details():
if "Module_str" in devices[d]:
for driver in dpdk_drivers:
if driver not in devices[d]["Module_str"]:
- devices[d]["Module_str"] = \
- devices[d]["Module_str"] + ",%s" % driver
+ devices[d]["Module_str"] = devices[d]["Module_str"] + ",%s" % driver
else:
devices[d]["Module_str"] = ",".join(dpdk_drivers)
@@ -350,9 +357,9 @@ def get_crypto_details():
def dev_id_from_dev_name(dev_name):
- '''Take a device "name" - a string passed in by user to identify a NIC
+ """Take a device "name" - a string passed in by user to identify a NIC
device, and determine the device id - i.e. the domain:bus:slot.func - for
- it, which can then be used to index into the devices array'''
+ it, which can then be used to index into the devices array"""
# check if it's already a suitable index
if dev_name in devices:
@@ -366,23 +373,29 @@ def dev_id_from_dev_name(dev_name):
if dev_name in devices[d]["Interface"].split(","):
return devices[d]["Slot"]
# if nothing else matches - error
- print("Unknown device: %s. "
- "Please specify device in \"bus:slot.func\" format" % dev_name)
+ print(
+ "Unknown device: %s. "
+ 'Please specify device in "bus:slot.func" format' % dev_name
+ )
sys.exit(1)
def unbind_one(dev_id, force):
- '''Unbind the device identified by "dev_id" from its current driver'''
+ """Unbind the device identified by "dev_id" from its current driver"""
dev = devices[dev_id]
if not has_driver(dev_id):
- print("%s %s %s is not currently managed by any driver\n" %
- (dev["Slot"], dev["Device_str"], dev["Interface"]))
+ print(
+ "%s %s %s is not currently managed by any driver\n"
+ % (dev["Slot"], dev["Device_str"], dev["Interface"])
+ )
return
# prevent us disconnecting ourselves
if dev["Ssh_if"] and not force:
- print("Routing table indicates that interface %s is active. "
- "Skipping unbind" % (dev_id))
+ print(
+ "Routing table indicates that interface %s is active. "
+ "Skipping unbind" % (dev_id)
+ )
return
# write to /sys to unbind
@@ -390,30 +403,30 @@ def unbind_one(dev_id, force):
try:
f = open(filename, "a")
except:
- print("Error: unbind failed for %s - Cannot open %s"
- % (dev_id, filename))
+ print("Error: unbind failed for %s - Cannot open %s" % (dev_id, filename))
sys.exit(1)
f.write(dev_id)
f.close()
def bind_one(dev_id, driver, force):
- '''Bind the device given by "dev_id" to the driver "driver". If the device
- is already bound to a different driver, it will be unbound first'''
+ """Bind the device given by "dev_id" to the driver "driver". If the device
+ is already bound to a different driver, it will be unbound first"""
dev = devices[dev_id]
saved_driver = None # used to rollback any unbind in case of failure
# prevent disconnection of our ssh session
if dev["Ssh_if"] and not force:
- print("Routing table indicates that interface %s is active. "
- "Not modifying" % (dev_id))
+ print(
+ "Routing table indicates that interface %s is active. "
+ "Not modifying" % (dev_id)
+ )
return
# unbind any existing drivers we don't want
if has_driver(dev_id):
if dev["Driver_str"] == driver:
- print("%s already bound to driver %s, skipping\n"
- % (dev_id, driver))
+ print("%s already bound to driver %s, skipping\n" % (dev_id, driver))
return
else:
saved_driver = dev["Driver_str"]
@@ -426,15 +439,16 @@ def bind_one(dev_id, driver, force):
try:
f = open(filename, "w")
except:
- print("Error: bind failed for %s - Cannot open %s"
- % (dev_id, filename))
+ print("Error: bind failed for %s - Cannot open %s" % (dev_id, filename))
return
try:
f.write("%04x %04x" % (dev["Vendor"], dev["Device"]))
f.close()
except:
- print("Error: bind failed for %s - Cannot write new PCI ID to "
- "driver %s" % (dev_id, driver))
+ print(
+ "Error: bind failed for %s - Cannot write new PCI ID to "
+ "driver %s" % (dev_id, driver)
+ )
return
# do the bind by writing to /sys
@@ -442,8 +456,7 @@ def bind_one(dev_id, driver, force):
try:
f = open(filename, "a")
except:
- print("Error: bind failed for %s - Cannot open %s"
- % (dev_id, filename))
+ print("Error: bind failed for %s - Cannot open %s" % (dev_id, filename))
if saved_driver is not None: # restore any previous driver
bind_one(dev_id, saved_driver, force)
return
@@ -457,8 +470,7 @@ def bind_one(dev_id, driver, force):
tmp = get_pci_device_details(dev_id)
if "Driver_str" in tmp and tmp["Driver_str"] == driver:
return
- print("Error: bind failed for %s - Cannot bind to driver %s"
- % (dev_id, driver))
+ print("Error: bind failed for %s - Cannot bind to driver %s" % (dev_id, driver))
if saved_driver is not None: # restore any previous driver
bind_one(dev_id, saved_driver, force)
return
@@ -491,8 +503,7 @@ def bind_all(dev_list, driver, force=False):
continue
# update information about this device
- devices[d] = dict(devices[d].items() +
- get_pci_device_details(d).items())
+ devices[d] = dict(devices[d].items() + get_pci_device_details(d).items())
# check if updated information indicates that the device was bound
if "Driver_str" in devices[d]:
@@ -500,20 +511,21 @@ def bind_all(dev_list, driver, force=False):
def display_devices(title, dev_list, extra_params=None):
- '''Displays to the user the details of a list of devices given in
+ """Displays to the user the details of a list of devices given in
"dev_list". The "extra_params" parameter, if given, should contain a string
with %()s fields in it for replacement by the named fields in each
- device's dictionary.'''
+ device's dictionary."""
strings = [] # this holds the strings to print. We sort before printing
print("\n%s" % title)
- print("="*len(title))
+ print("=" * len(title))
if len(dev_list) == 0:
strings.append("<none>")
else:
for dev in dev_list:
if extra_params is not None:
- strings.append("%s '%s' %s" % (dev["Slot"],
- dev["Device_str"], extra_params % dev))
+ strings.append(
+ "%s '%s' %s" % (dev["Slot"], dev["Device_str"], extra_params % dev)
+ )
else:
strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
# sort before printing, so that the entries appear in PCI order
@@ -522,9 +534,9 @@ def display_devices(title, dev_list, extra_params=None):
def show_status():
- '''Function called when the script is passed the "--status" option.
+ """Function called when the script is passed the "--status" option.
Displays to the user what devices are bound to the igb_uio driver, the
- kernel driver or to no driver'''
+ kernel driver or to no driver"""
global dpdk_drivers
kernel_drv = []
dpdk_drv = []
@@ -532,7 +544,7 @@ def show_status():
# split our list of network devices into the three categories above
for d in devices.keys():
- if (NETWORK_BASE_CLASS in devices[d]["Class"]):
+ if NETWORK_BASE_CLASS in devices[d]["Class"]:
if not has_driver(d):
no_drv.append(devices[d])
continue
@@ -542,11 +554,16 @@ def show_status():
kernel_drv.append(devices[d])
# print each category separately, so we can clearly see what's used by DPDK
- display_devices("Network devices using DPDK-compatible driver", dpdk_drv,
- "drv=%(Driver_str)s unused=%(Module_str)s")
- display_devices("Network devices using kernel driver", kernel_drv,
- "if=%(Interface)s drv=%(Driver_str)s "
- "unused=%(Module_str)s %(Active)s")
+ display_devices(
+ "Network devices using DPDK-compatible driver",
+ dpdk_drv,
+ "drv=%(Driver_str)s unused=%(Module_str)s",
+ )
+ display_devices(
+ "Network devices using kernel driver",
+ kernel_drv,
+ "if=%(Interface)s drv=%(Driver_str)s " "unused=%(Module_str)s %(Active)s",
+ )
display_devices("Other network devices", no_drv, "unused=%(Module_str)s")
# split our list of crypto devices into the three categories above
@@ -555,7 +572,7 @@ def show_status():
no_drv = []
for d in devices.keys():
- if (CRYPTO_BASE_CLASS in devices[d]["Class"]):
+ if CRYPTO_BASE_CLASS in devices[d]["Class"]:
if not has_driver(d):
no_drv.append(devices[d])
continue
@@ -564,17 +581,22 @@ def show_status():
else:
kernel_drv.append(devices[d])
- display_devices("Crypto devices using DPDK-compatible driver", dpdk_drv,
- "drv=%(Driver_str)s unused=%(Module_str)s")
- display_devices("Crypto devices using kernel driver", kernel_drv,
- "drv=%(Driver_str)s "
- "unused=%(Module_str)s")
+ display_devices(
+ "Crypto devices using DPDK-compatible driver",
+ dpdk_drv,
+ "drv=%(Driver_str)s unused=%(Module_str)s",
+ )
+ display_devices(
+ "Crypto devices using kernel driver",
+ kernel_drv,
+ "drv=%(Driver_str)s " "unused=%(Module_str)s",
+ )
display_devices("Other crypto devices", no_drv, "unused=%(Module_str)s")
def parse_args():
- '''Parses the command-line arguments given by the user and takes the
- appropriate action for each'''
+ """Parses the command-line arguments given by the user and takes the
+ appropriate action for each"""
global b_flag
global status_flag
global force_flag
@@ -584,9 +606,11 @@ def parse_args():
sys.exit(0)
try:
- opts, args = getopt.getopt(sys.argv[1:], "b:us",
- ["help", "usage", "status", "force",
- "bind=", "unbind"])
+ opts, args = getopt.getopt(
+ sys.argv[1:],
+ "b:us",
+ ["help", "usage", "status", "force", "bind=", "unbind"],
+ )
except getopt.GetoptError as error:
print(str(error))
print("Run '%s --usage' for further information" % sys.argv[0])
@@ -611,15 +635,14 @@ def parse_args():
def do_arg_actions():
- '''do the actual action requested by the user'''
+ """do the actual action requested by the user"""
global b_flag
global status_flag
global force_flag
global args
if b_flag is None and not status_flag:
- print("Error: No action specified for devices."
- "Please give a -b or -u option")
+ print("Error: No action specified for devices." "Please give a -b or -u option")
print("Run '%s --usage' for further information" % sys.argv[0])
sys.exit(1)
@@ -640,7 +663,7 @@ def do_arg_actions():
def main():
- '''program main function'''
+ """program main function"""
parse_args()
check_modules()
get_nic_details()
diff --git a/extras/vpp_config/setup.py b/extras/vpp_config/setup.py
index 64f75d7cc89..010920bd7d5 100644
--- a/extras/vpp_config/setup.py
+++ b/extras/vpp_config/setup.py
@@ -1,33 +1,31 @@
from setuptools import setup
-setup(name="vpp_config",
- version="20.05.1",
- author="John DeNisco",
- author_email="jdenisco@cisco.com",
- description="VPP Configuration Utility",
- license='Apache-2.0',
- keywords="vppconfig",
- url='https://wiki.fd.io/view/VPP',
- py_modules=['vpp_config'],
- install_requires=['distro', 'pyyaml', 'requests'],
- extra_requires=["ipaddress; python_version < '3.3'"],
- packages=['vpplib'],
- entry_points={
- 'console_scripts': ['vpp-config=vpp_config:config_main'],
- },
- data_files=[('vpp/vpp-config/scripts', ['scripts/dpdk-devbind.py']),
- ('vpp/vpp-config/configs', ['data/auto-config.yaml']),
- ('vpp/vpp-config/configs', ['data/cloud-config.iso']),
- ('vpp/vpp-config/configs',
- ['data/iperf-centos.xml.template']),
- ('vpp/vpp-config/configs',
- ['data/iperf-ubuntu.xml.template']),
- ('vpp/vpp-config/dryrun/sysctl.d',
- ['data/80-vpp.conf.template']),
- ('vpp/vpp-config/dryrun/default', ['data/grub.template']),
- ('vpp/vpp-config/dryrun/vpp',
- ['data/startup.conf.template']),
- ],
- long_description="The VPP configuration utility can be used to "
- "easily configure VPP.",
- )
+setup(
+ name="vpp_config",
+ version="20.05.1",
+ author="John DeNisco",
+ author_email="jdenisco@cisco.com",
+ description="VPP Configuration Utility",
+ license="Apache-2.0",
+ keywords="vppconfig",
+ url="https://wiki.fd.io/view/VPP",
+ py_modules=["vpp_config"],
+ install_requires=["distro", "pyyaml", "requests"],
+ extra_requires=["ipaddress; python_version < '3.3'"],
+ packages=["vpplib"],
+ entry_points={
+ "console_scripts": ["vpp-config=vpp_config:config_main"],
+ },
+ data_files=[
+ ("vpp/vpp-config/scripts", ["scripts/dpdk-devbind.py"]),
+ ("vpp/vpp-config/configs", ["data/auto-config.yaml"]),
+ ("vpp/vpp-config/configs", ["data/cloud-config.iso"]),
+ ("vpp/vpp-config/configs", ["data/iperf-centos.xml.template"]),
+ ("vpp/vpp-config/configs", ["data/iperf-ubuntu.xml.template"]),
+ ("vpp/vpp-config/dryrun/sysctl.d", ["data/80-vpp.conf.template"]),
+ ("vpp/vpp-config/dryrun/default", ["data/grub.template"]),
+ ("vpp/vpp-config/dryrun/vpp", ["data/startup.conf.template"]),
+ ],
+ long_description="The VPP configuration utility can be used to "
+ "easily configure VPP.",
+)
diff --git a/extras/vpp_config/vpp_config.py b/extras/vpp_config/vpp_config.py
index e863cde854e..74051300677 100755
--- a/extras/vpp_config/vpp_config.py
+++ b/extras/vpp_config/vpp_config.py
@@ -32,16 +32,16 @@ try:
except NameError:
pass
-VPP_DRYRUNDIR = '/vpp/vpp-config/dryrun'
-VPP_AUTO_CONFIGURATION_FILE = '/vpp/vpp-config/configs/auto-config.yaml'
-VPP_HUGE_PAGE_FILE = '/vpp/vpp-config/dryrun/sysctl.d/80-vpp.conf'
-VPP_STARTUP_FILE = '/vpp/vpp-config/dryrun/vpp/startup.conf'
-VPP_GRUB_FILE = '/vpp/vpp-config/dryrun/default/grub'
-VPP_REAL_HUGE_PAGE_FILE = '/etc/sysctl.d/80-vpp.conf'
-VPP_REAL_STARTUP_FILE = '/etc/vpp/startup.conf'
-VPP_REAL_GRUB_FILE = '/etc/default/grub'
+VPP_DRYRUNDIR = "/vpp/vpp-config/dryrun"
+VPP_AUTO_CONFIGURATION_FILE = "/vpp/vpp-config/configs/auto-config.yaml"
+VPP_HUGE_PAGE_FILE = "/vpp/vpp-config/dryrun/sysctl.d/80-vpp.conf"
+VPP_STARTUP_FILE = "/vpp/vpp-config/dryrun/vpp/startup.conf"
+VPP_GRUB_FILE = "/vpp/vpp-config/dryrun/default/grub"
+VPP_REAL_HUGE_PAGE_FILE = "/etc/sysctl.d/80-vpp.conf"
+VPP_REAL_STARTUP_FILE = "/etc/vpp/startup.conf"
+VPP_REAL_GRUB_FILE = "/etc/default/grub"
-rootdir = ''
+rootdir = ""
def autoconfig_yn(question, default):
@@ -57,16 +57,16 @@ def autoconfig_yn(question, default):
"""
input_valid = False
default = default.lower()
- answer = ''
+ answer = ""
while not input_valid:
answer = input(question)
if len(answer) == 0:
answer = default
- if re.findall(r'[YyNn]', answer):
+ if re.findall(r"[YyNn]", answer):
input_valid = True
answer = answer[0].lower()
else:
- print ("Please answer Y, N or Return.")
+ print("Please answer Y, N or Return.")
return answer
@@ -86,24 +86,21 @@ def autoconfig_cp(node, src, dst):
# If the destination file exist, create a copy if one does not already
# exist
- ofile = dst + '.orig'
- (ret, stdout, stderr) = VPPUtil.exec_command('ls {}'.format(dst))
+ ofile = dst + ".orig"
+ (ret, stdout, stderr) = VPPUtil.exec_command("ls {}".format(dst))
if ret == 0:
- cmd = 'cp {} {}'.format(dst, ofile)
+ cmd = "cp {} {}".format(dst, ofile)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd,
- node['host'],
- stdout,
- stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
# Copy the source file
- cmd = 'cp {} {}'.format(src, os.path.dirname(dst))
+ cmd = "cp {} {}".format(src, os.path.dirname(dst))
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.
- format(cmd, node['host'], stderr))
+ raise RuntimeError("{} failed on node {} {}".format(cmd, node["host"], stderr))
def autoconfig_diff(node, src, dst):
@@ -124,12 +121,10 @@ def autoconfig_diff(node, src, dst):
# Diff the files and return the output
cmd = "diff {} {}".format(src, dst)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
- if stderr != '':
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd,
- node['host'],
- ret,
- stderr))
+ if stderr != "":
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], ret, stderr)
+ )
return stdout
@@ -160,13 +155,15 @@ def autoconfig_hugepage_apply(node, ask_questions=True):
"""
diffs = autoconfig_diff(node, VPP_REAL_HUGE_PAGE_FILE, rootdir + VPP_HUGE_PAGE_FILE)
- if diffs != '':
- print ("These are the changes we will apply to")
- print ("the huge page file ({}).\n".format(VPP_REAL_HUGE_PAGE_FILE))
- print (diffs)
+ if diffs != "":
+ print("These are the changes we will apply to")
+ print("the huge page file ({}).\n".format(VPP_REAL_HUGE_PAGE_FILE))
+ print(diffs)
if ask_questions:
- answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y')
- if answer == 'n':
+ answer = autoconfig_yn(
+ "\nAre you sure you want to apply these changes [Y/n]? ", "y"
+ )
+ if answer == "n":
return -1
# Copy and sysctl
@@ -174,10 +171,11 @@ def autoconfig_hugepage_apply(node, ask_questions=True):
cmd = "sysctl -p {}".format(VPP_REAL_HUGE_PAGE_FILE)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'], stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
else:
- print ('\nThere are no changes to the huge page configuration.')
+ print("\nThere are no changes to the huge page configuration.")
return 0
@@ -196,19 +194,21 @@ def autoconfig_vpp_apply(node, ask_questions=True):
"""
diffs = autoconfig_diff(node, VPP_REAL_STARTUP_FILE, rootdir + VPP_STARTUP_FILE)
- if diffs != '':
- print ("These are the changes we will apply to")
- print ("the VPP startup file ({}).\n".format(VPP_REAL_STARTUP_FILE))
- print (diffs)
+ if diffs != "":
+ print("These are the changes we will apply to")
+ print("the VPP startup file ({}).\n".format(VPP_REAL_STARTUP_FILE))
+ print(diffs)
if ask_questions:
- answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y')
- if answer == 'n':
+ answer = autoconfig_yn(
+ "\nAre you sure you want to apply these changes [Y/n]? ", "y"
+ )
+ if answer == "n":
return -1
# Copy the VPP startup
autoconfig_cp(node, rootdir + VPP_STARTUP_FILE, VPP_REAL_STARTUP_FILE)
else:
- print ('\nThere are no changes to VPP startup.')
+ print("\nThere are no changes to VPP startup.")
return 0
@@ -226,49 +226,52 @@ def autoconfig_grub_apply(node, ask_questions=True):
"""
- print ("\nThe configured grub cmdline looks like this:")
- configured_cmdline = node['grub']['default_cmdline']
- current_cmdline = node['grub']['current_cmdline']
- print (configured_cmdline)
- print ("\nThe current boot cmdline looks like this:")
- print (current_cmdline)
+ print("\nThe configured grub cmdline looks like this:")
+ configured_cmdline = node["grub"]["default_cmdline"]
+ current_cmdline = node["grub"]["current_cmdline"]
+ print(configured_cmdline)
+ print("\nThe current boot cmdline looks like this:")
+ print(current_cmdline)
if ask_questions:
question = "\nDo you want to keep the current boot cmdline [Y/n]? "
- answer = autoconfig_yn(question, 'y')
- if answer == 'y':
+ answer = autoconfig_yn(question, "y")
+ if answer == "y":
return
- node['grub']['keep_cmdline'] = False
+ node["grub"]["keep_cmdline"] = False
# Diff the file
diffs = autoconfig_diff(node, VPP_REAL_GRUB_FILE, rootdir + VPP_GRUB_FILE)
- if diffs != '':
- print ("These are the changes we will apply to")
- print ("the GRUB file ({}).\n".format(VPP_REAL_GRUB_FILE))
- print (diffs)
+ if diffs != "":
+ print("These are the changes we will apply to")
+ print("the GRUB file ({}).\n".format(VPP_REAL_GRUB_FILE))
+ print(diffs)
if ask_questions:
- answer = autoconfig_yn("\nAre you sure you want to apply these changes [y/N]? ", 'n')
- if answer == 'n':
+ answer = autoconfig_yn(
+ "\nAre you sure you want to apply these changes [y/N]? ", "n"
+ )
+ if answer == "n":
return -1
# Copy and update grub
autoconfig_cp(node, rootdir + VPP_GRUB_FILE, VPP_REAL_GRUB_FILE)
distro = VPPUtil.get_linux_distro()
- if distro[0] == 'Ubuntu':
+ if distro[0] == "Ubuntu":
cmd = "update-grub"
else:
cmd = "grub2-mkconfig -o /boot/grub2/grub.cfg"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'], stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
- print ("There have been changes to the GRUB config a", end=' ')
- print ("reboot will be required.")
+ print("There have been changes to the GRUB config a", end=" ")
+ print("reboot will be required.")
return -1
else:
- print ('\nThere are no changes to the GRUB config.')
+ print("\nThere are no changes to the GRUB config.")
return 0
@@ -289,15 +292,15 @@ def autoconfig_apply(ask_questions=True):
vutil = VPPUtil()
pkgs = vutil.get_installed_vpp_pkgs()
if len(pkgs) == 0:
- print ("\nVPP is not installed, Install VPP with option 4.")
+ print("\nVPP is not installed, Install VPP with option 4.")
return
acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
if ask_questions:
- print ("\nWe are now going to configure your system(s).\n")
- answer = autoconfig_yn("Are you sure you want to do this [Y/n]? ", 'y')
- if answer == 'n':
+ print("\nWe are now going to configure your system(s).\n")
+ answer = autoconfig_yn("Are you sure you want to do this [Y/n]? ", "y")
+ if answer == "n":
return
nodes = acfg.get_nodes()
@@ -369,7 +372,9 @@ def autoconfig_dryrun(ask_questions=True):
for i in nodes.items():
node = i[1]
if not acfg.has_interfaces(node):
- print("\nThere are no VPP interfaces configured, please configure at least 1.")
+ print(
+ "\nThere are no VPP interfaces configured, please configure at least 1."
+ )
return
# Modify CPU
@@ -414,34 +419,31 @@ def autoconfig_install():
pkgs = vutil.get_installed_vpp_pkgs()
if len(pkgs) > 0:
- print ("\nThese packages are installed on node {}"
- .format(node['host']))
- print ("{:25} {}".format("Name", "Version"))
+ print("\nThese packages are installed on node {}".format(node["host"]))
+ print("{:25} {}".format("Name", "Version"))
for pkg in pkgs:
try:
- print ("{:25} {}".format(
- pkg['name'], pkg['version']))
+ print("{:25} {}".format(pkg["name"], pkg["version"]))
except KeyError:
- print ("{}".format(pkg['name']))
+ print("{}".format(pkg["name"]))
question = "\nDo you want to uninstall these "
question += "packages [y/N]? "
- answer = autoconfig_yn(question, 'n')
- if answer == 'y':
+ answer = autoconfig_yn(question, "n")
+ if answer == "y":
logger.setLevel(logging.INFO)
vutil.uninstall_vpp(node)
else:
- print ("\nThere are no VPP packages on node {}."
- .format(node['host']))
+ print("\nThere are no VPP packages on node {}.".format(node["host"]))
question = "Do you want to install VPP [Y/n]? "
- answer = autoconfig_yn(question, 'y')
- if answer == 'y':
+ answer = autoconfig_yn(question, "y")
+ if answer == "y":
question = "Do you want to install the release version [Y/n]? "
- answer = autoconfig_yn(question, 'y')
- if answer == 'y':
- branch = 'release'
+ answer = autoconfig_yn(question, "y")
+ if answer == "y":
+ branch = "release"
else:
- branch = 'master'
+ branch = "master"
logger.setLevel(logging.INFO)
vutil.install_vpp(node, branch)
@@ -486,9 +488,9 @@ def autoconfig_create_iperf_vm():
"""
acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
- acfg.destroy_iperf_vm('iperf-server')
+ acfg.destroy_iperf_vm("iperf-server")
acfg.create_and_bridge_iperf_virtual_interface()
- acfg.create_iperf_vm('iperf-server')
+ acfg.create_iperf_vm("iperf-server")
def autoconfig_not_implemented():
@@ -497,7 +499,7 @@ def autoconfig_not_implemented():
"""
- print ("\nThis Feature is not implemented yet....")
+ print("\nThis Feature is not implemented yet....")
def autoconfig_basic_test_menu():
@@ -506,28 +508,28 @@ def autoconfig_basic_test_menu():
"""
- basic_menu_text = '\nWhat would you like to do?\n\n\
+ basic_menu_text = "\nWhat would you like to do?\n\n\
1) List/Create Simple IPv4 Setup\n\
2) Create an iperf VM and Connect to VPP an interface\n\
-9 or q) Back to main menu.'
+9 or q) Back to main menu."
- print ("{}".format(basic_menu_text))
+ print("{}".format(basic_menu_text))
input_valid = False
- answer = ''
+ answer = ""
while not input_valid:
answer = input("\nCommand: ")
if len(answer) > 1:
- print ("Please enter only 1 character.")
+ print("Please enter only 1 character.")
continue
- if re.findall(r'[Qq1-29]', answer):
+ if re.findall(r"[Qq1-29]", answer):
input_valid = True
answer = answer[0].lower()
else:
- print ("Please enter a character between 1 and 2 or 9.")
+ print("Please enter a character between 1 and 2 or 9.")
- if answer == '9':
- answer = 'q'
+ if answer == "9":
+ answer = "q"
return answer
@@ -540,17 +542,17 @@ def autoconfig_basic_test():
vutil = VPPUtil()
pkgs = vutil.get_installed_vpp_pkgs()
if len(pkgs) == 0:
- print ("\nVPP is not installed, install VPP with option 4.")
+ print("\nVPP is not installed, install VPP with option 4.")
return
- answer = ''
- while answer != 'q':
+ answer = ""
+ while answer != "q":
answer = autoconfig_basic_test_menu()
- if answer == '1':
+ if answer == "1":
autoconfig_ipv4_setup()
- elif answer == '2':
+ elif answer == "2":
autoconfig_create_iperf_vm()
- elif answer == '9' or answer == 'q':
+ elif answer == "9" or answer == "q":
return
else:
autoconfig_not_implemented()
@@ -562,30 +564,32 @@ def autoconfig_main_menu():
"""
- main_menu_text = '\nWhat would you like to do?\n\n\
+ main_menu_text = "\nWhat would you like to do?\n\n\
1) Show basic system information\n\
2) Dry Run (Saves the configuration files in {}/vpp/vpp-config/dryrun.\n\
3) Full configuration (WARNING: This will change the system configuration)\n\
4) List/Install/Uninstall VPP.\n\
-q) Quit'.format(rootdir, rootdir)
+q) Quit".format(
+ rootdir, rootdir
+ )
# 5) Dry Run from {}/vpp/vpp-config/auto-config.yaml (will not ask questions).\n\
# 6) Install QEMU patch (Needed when running openstack).\n\
- print ("{}".format(main_menu_text))
+ print("{}".format(main_menu_text))
input_valid = False
- answer = ''
+ answer = ""
while not input_valid:
answer = input("\nCommand: ")
if len(answer) > 1:
- print ("Please enter only 1 character.")
+ print("Please enter only 1 character.")
continue
- if re.findall(r'[Qq1-4]', answer):
+ if re.findall(r"[Qq1-4]", answer):
input_valid = True
answer = answer[0].lower()
else:
- print ("Please enter a character between 1 and 4 or q.")
+ print("Please enter a character between 1 and 4 or q.")
return answer
@@ -599,18 +603,18 @@ def autoconfig_main():
# Setup
autoconfig_setup()
- answer = ''
- while answer != 'q':
+ answer = ""
+ while answer != "q":
answer = autoconfig_main_menu()
- if answer == '1':
+ if answer == "1":
autoconfig_show_system()
- elif answer == '2':
+ elif answer == "2":
autoconfig_dryrun()
- elif answer == '3':
+ elif answer == "3":
autoconfig_apply()
- elif answer == '4':
+ elif answer == "4":
autoconfig_install()
- elif answer == 'q':
+ elif answer == "q":
return
else:
autoconfig_not_implemented()
@@ -627,53 +631,65 @@ def autoconfig_setup(ask_questions=True):
global rootdir
distro = VPPUtil.get_linux_distro()
- if distro[0] == 'Ubuntu':
- rootdir = '/usr/local'
+ if distro[0] == "Ubuntu":
+ rootdir = "/usr/local"
else:
- rootdir = '/usr'
+ rootdir = "/usr"
# If there is a system configuration file use that, if not use the initial auto-config file
filename = rootdir + VPP_AUTO_CONFIGURATION_FILE
if os.path.isfile(filename) is True:
acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
else:
- raise RuntimeError('The Auto configuration file does not exist {}'.
- format(filename))
+ raise RuntimeError(
+ "The Auto configuration file does not exist {}".format(filename)
+ )
if ask_questions:
- print ("\nWelcome to the VPP system configuration utility")
+ print("\nWelcome to the VPP system configuration utility")
- print ("\nThese are the files we will modify:")
- print (" /etc/vpp/startup.conf")
- print (" /etc/sysctl.d/80-vpp.conf")
- print (" /etc/default/grub")
+ print("\nThese are the files we will modify:")
+ print(" /etc/vpp/startup.conf")
+ print(" /etc/sysctl.d/80-vpp.conf")
+ print(" /etc/default/grub")
- print (
+ print(
"\nBefore we change them, we'll create working copies in "
- "{}".format(rootdir + VPP_DRYRUNDIR))
- print (
+ "{}".format(rootdir + VPP_DRYRUNDIR)
+ )
+ print(
"Please inspect them carefully before applying the actual "
- "configuration (option 3)!")
+ "configuration (option 3)!"
+ )
nodes = acfg.get_nodes()
for i in nodes.items():
node = i[1]
- if (os.path.isfile(rootdir + VPP_STARTUP_FILE) is not True) and \
- (os.path.isfile(VPP_REAL_STARTUP_FILE) is True):
- autoconfig_cp(node, VPP_REAL_STARTUP_FILE, '{}'.format(rootdir + VPP_STARTUP_FILE))
- if (os.path.isfile(rootdir + VPP_HUGE_PAGE_FILE) is not True) and \
- (os.path.isfile(VPP_REAL_HUGE_PAGE_FILE) is True):
- autoconfig_cp(node, VPP_REAL_HUGE_PAGE_FILE, '{}'.format(rootdir + VPP_HUGE_PAGE_FILE))
- if (os.path.isfile(rootdir + VPP_GRUB_FILE) is not True) and \
- (os.path.isfile(VPP_REAL_GRUB_FILE) is True):
- autoconfig_cp(node, VPP_REAL_GRUB_FILE, '{}'.format(rootdir + VPP_GRUB_FILE))
+ if (os.path.isfile(rootdir + VPP_STARTUP_FILE) is not True) and (
+ os.path.isfile(VPP_REAL_STARTUP_FILE) is True
+ ):
+ autoconfig_cp(
+ node, VPP_REAL_STARTUP_FILE, "{}".format(rootdir + VPP_STARTUP_FILE)
+ )
+ if (os.path.isfile(rootdir + VPP_HUGE_PAGE_FILE) is not True) and (
+ os.path.isfile(VPP_REAL_HUGE_PAGE_FILE) is True
+ ):
+ autoconfig_cp(
+ node, VPP_REAL_HUGE_PAGE_FILE, "{}".format(rootdir + VPP_HUGE_PAGE_FILE)
+ )
+ if (os.path.isfile(rootdir + VPP_GRUB_FILE) is not True) and (
+ os.path.isfile(VPP_REAL_GRUB_FILE) is True
+ ):
+ autoconfig_cp(
+ node, VPP_REAL_GRUB_FILE, "{}".format(rootdir + VPP_GRUB_FILE)
+ )
# Be sure the uio_pci_generic driver is installed
- cmd = 'modprobe uio_pci_generic'
+ cmd = "modprobe uio_pci_generic"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- logging.warning('{} failed on node {} {}'. format(cmd, node['host'], stderr))
+ logging.warning("{} failed on node {} {}".format(cmd, node["host"], stderr))
# noinspection PyUnresolvedReferences
@@ -707,10 +723,9 @@ def config_main():
# Check for root
if not os.geteuid() == 0:
- sys.exit('\nPlease run the VPP Configuration Utility as root.')
+ sys.exit("\nPlease run the VPP Configuration Utility as root.")
- if len(sys.argv) > 1 and ((sys.argv[1] == '-d') or (
- sys.argv[1] == '--debug')):
+ if len(sys.argv) > 1 and ((sys.argv[1] == "-d") or (sys.argv[1] == "--debug")):
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.ERROR)
@@ -720,37 +735,44 @@ def config_main():
if len(sys.argv) == 1:
autoconfig_main()
return
- elif len(sys.argv) == 2 and ((sys.argv[1] == '-d') or (
- sys.argv[1] == '--debug')):
+ elif len(sys.argv) == 2 and ((sys.argv[1] == "-d") or (sys.argv[1] == "--debug")):
autoconfig_main()
return
# There were arguments specified, so execute the utility using
# command line arguments
- description = 'The VPP configuration utility allows the user to '
- 'configure VPP in a simple and safe manner. The utility takes input '
- 'from the user or the specified .yaml file. The user should then '
- 'examine these files to be sure they are correct and then actually '
- 'apply the configuration. When run without arguments the utility run '
- 'in an interactive mode'
+ description = "The VPP configuration utility allows the user to "
+ "configure VPP in a simple and safe manner. The utility takes input "
+ "from the user or the specified .yaml file. The user should then "
+ "examine these files to be sure they are correct and then actually "
+ "apply the configuration. When run without arguments the utility run "
+ "in an interactive mode"
main_parser = argparse.ArgumentParser(
- prog='arg-test',
+ prog="arg-test",
description=description,
- epilog='See "%(prog)s help COMMAND" for help on a specific command.')
- main_parser.add_argument('--apply', '-a', action='store_true',
- help='Apply the cofiguration.')
- main_parser.add_argument('--dry-run', '-dr', action='store_true',
- help='Create the dryrun configuration files.')
- main_parser.add_argument('--show', '-s', action='store_true',
- help='Shows basic system information')
- main_parser.add_argument('--debug', '-d', action='count',
- help='Print debug output (multiple levels)')
+ epilog='See "%(prog)s help COMMAND" for help on a specific command.',
+ )
+ main_parser.add_argument(
+ "--apply", "-a", action="store_true", help="Apply the cofiguration."
+ )
+ main_parser.add_argument(
+ "--dry-run",
+ "-dr",
+ action="store_true",
+ help="Create the dryrun configuration files.",
+ )
+ main_parser.add_argument(
+ "--show", "-s", action="store_true", help="Shows basic system information"
+ )
+ main_parser.add_argument(
+ "--debug", "-d", action="count", help="Print debug output (multiple levels)"
+ )
args = main_parser.parse_args()
return execute_with_args(args)
-if __name__ == '__main__':
+if __name__ == "__main__":
config_main()
diff --git a/extras/vpp_config/vpplib/AutoConfig.py b/extras/vpp_config/vpplib/AutoConfig.py
index 62f18e27929..9a79039f69e 100644
--- a/extras/vpp_config/vpplib/AutoConfig.py
+++ b/extras/vpp_config/vpplib/AutoConfig.py
@@ -41,9 +41,9 @@ MIN_SYSTEM_CPUS = 2
MIN_TOTAL_HUGE_PAGES = 1024
MAX_PERCENT_FOR_HUGE_PAGES = 70
-IPERFVM_XML = 'configs/iperf-vm.xml'
-IPERFVM_IMAGE = 'images/xenial-mod.img'
-IPERFVM_ISO = 'configs/cloud-config.iso'
+IPERFVM_XML = "configs/iperf-vm.xml"
+IPERFVM_IMAGE = "images/xenial-mod.img"
+IPERFVM_ISO = "configs/cloud-config.iso"
class AutoConfig(object):
@@ -90,12 +90,12 @@ class AutoConfig(object):
"""
# Does a copy of the file exist, if not create one
- ofile = filename + '.orig'
- (ret, stdout, stderr) = VPPUtil.exec_command('ls {}'.format(ofile))
+ ofile = filename + ".orig"
+ (ret, stdout, stderr) = VPPUtil.exec_command("ls {}".format(ofile))
if ret != 0:
logging.debug(stderr)
- if stdout.strip('\n') != ofile:
- cmd = 'sudo cp {} {}'.format(filename, ofile)
+ if stdout.strip("\n") != ofile:
+ cmd = "sudo cp {} {}".format(filename, ofile)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
@@ -114,14 +114,14 @@ class AutoConfig(object):
while True:
answer = input("Please enter the IPv4 Address [n.n.n.n/n]: ")
try:
- ipinput = answer.split('/')
+ ipinput = answer.split("/")
ipaddr = ip_address(ipinput[0])
if len(ipinput) > 1:
- plen = answer.split('/')[1]
+ plen = answer.split("/")[1]
else:
answer = input("Please enter the netmask [n.n.n.n]: ")
plen = ip_address(answer).netmask_bits()
- return '{}/{}'.format(ipaddr, plen)
+ return "{}/{}".format(ipaddr, plen)
except ValueError:
print("Please enter a valid IPv4 address.")
@@ -145,18 +145,22 @@ class AutoConfig(object):
while True:
answer = input(question)
- if answer == '':
+ if answer == "":
answer = default
break
- if re.findall(r'[0-9+]', answer):
+ if re.findall(r"[0-9+]", answer):
if int(answer) in range(first, last + 1):
break
else:
- print("Please a value between {} and {} or Return.".
- format(first, last))
+ print(
+ "Please a value between {} and {} or Return.".format(
+ first, last
+ )
+ )
else:
- print("Please a number between {} and {} or Return.".
- format(first, last))
+ print(
+ "Please a number between {} and {} or Return.".format(first, last)
+ )
return int(answer)
@@ -175,12 +179,12 @@ class AutoConfig(object):
input_valid = False
default = default.lower()
- answer = ''
+ answer = ""
while not input_valid:
answer = input(question)
- if answer == '':
+ if answer == "":
answer = default
- if re.findall(r'[YyNn]', answer):
+ if re.findall(r"[YyNn]", answer):
input_valid = True
answer = answer[0].lower()
else:
@@ -196,36 +200,40 @@ class AutoConfig(object):
# Get the Topology, from the topology layout file
topo = {}
- with open(self._autoconfig_filename, 'r') as stream:
+ with open(self._autoconfig_filename, "r") as stream:
try:
topo = yaml.load(stream)
- if 'metadata' in topo:
- self._metadata = topo['metadata']
+ if "metadata" in topo:
+ self._metadata = topo["metadata"]
except yaml.YAMLError as exc:
raise RuntimeError(
"Couldn't read the Auto config file {}.".format(
- self._autoconfig_filename, exc))
+ self._autoconfig_filename, exc
+ )
+ )
- systemfile = self._rootdir + self._metadata['system_config_file']
+ systemfile = self._rootdir + self._metadata["system_config_file"]
if self._clean is False and os.path.isfile(systemfile):
- with open(systemfile, 'r') as sysstream:
+ with open(systemfile, "r") as sysstream:
try:
systopo = yaml.load(sysstream)
- if 'nodes' in systopo:
- self._nodes = systopo['nodes']
+ if "nodes" in systopo:
+ self._nodes = systopo["nodes"]
except yaml.YAMLError as sysexc:
raise RuntimeError(
"Couldn't read the System config file {}.".format(
- systemfile, sysexc))
+ systemfile, sysexc
+ )
+ )
else:
# Get the nodes from Auto Config
- if 'nodes' in topo:
- self._nodes = topo['nodes']
+ if "nodes" in topo:
+ self._nodes = topo["nodes"]
# Set the root directory in all the nodes
for i in self._nodes.items():
node = i[1]
- node['rootdir'] = self._rootdir
+ node["rootdir"] = self._rootdir
def updateconfig(self):
"""
@@ -236,11 +244,11 @@ class AutoConfig(object):
"""
# Initialize the yaml data
- ydata = {'metadata': self._metadata, 'nodes': self._nodes}
+ ydata = {"metadata": self._metadata, "nodes": self._nodes}
# Write the system config file
- filename = self._rootdir + self._metadata['system_config_file']
- with open(filename, 'w') as yamlfile:
+ filename = self._rootdir + self._metadata["system_config_file"]
+ with open(filename, "w") as yamlfile:
yaml.dump(ydata, yamlfile)
def _update_auto_config(self):
@@ -252,11 +260,11 @@ class AutoConfig(object):
# Initialize the yaml data
nodes = {}
- with open(self._autoconfig_filename, 'r') as stream:
+ with open(self._autoconfig_filename, "r") as stream:
try:
ydata = yaml.load(stream)
- if 'nodes' in ydata:
- nodes = ydata['nodes']
+ if "nodes" in ydata:
+ nodes = ydata["nodes"]
except yaml.YAMLError as exc:
print(exc)
return
@@ -266,41 +274,45 @@ class AutoConfig(object):
node = i[1]
# Interfaces
- node['interfaces'] = {}
- for item in self._nodes[key]['interfaces'].items():
+ node["interfaces"] = {}
+ for item in self._nodes[key]["interfaces"].items():
port = item[0]
interface = item[1]
- node['interfaces'][port] = {}
- addr = '{}'.format(interface['pci_address'])
- node['interfaces'][port]['pci_address'] = addr
- if 'mac_address' in interface:
- node['interfaces'][port]['mac_address'] = \
- interface['mac_address']
-
- if 'total_other_cpus' in self._nodes[key]['cpu']:
- node['cpu']['total_other_cpus'] = \
- self._nodes[key]['cpu']['total_other_cpus']
- if 'total_vpp_cpus' in self._nodes[key]['cpu']:
- node['cpu']['total_vpp_cpus'] = \
- self._nodes[key]['cpu']['total_vpp_cpus']
- if 'reserve_vpp_main_core' in self._nodes[key]['cpu']:
- node['cpu']['reserve_vpp_main_core'] = \
- self._nodes[key]['cpu']['reserve_vpp_main_core']
+ node["interfaces"][port] = {}
+ addr = "{}".format(interface["pci_address"])
+ node["interfaces"][port]["pci_address"] = addr
+ if "mac_address" in interface:
+ node["interfaces"][port]["mac_address"] = interface["mac_address"]
+
+ if "total_other_cpus" in self._nodes[key]["cpu"]:
+ node["cpu"]["total_other_cpus"] = self._nodes[key]["cpu"][
+ "total_other_cpus"
+ ]
+ if "total_vpp_cpus" in self._nodes[key]["cpu"]:
+ node["cpu"]["total_vpp_cpus"] = self._nodes[key]["cpu"][
+ "total_vpp_cpus"
+ ]
+ if "reserve_vpp_main_core" in self._nodes[key]["cpu"]:
+ node["cpu"]["reserve_vpp_main_core"] = self._nodes[key]["cpu"][
+ "reserve_vpp_main_core"
+ ]
# TCP
- if 'active_open_sessions' in self._nodes[key]['tcp']:
- node['tcp']['active_open_sessions'] = \
- self._nodes[key]['tcp']['active_open_sessions']
- if 'passive_open_sessions' in self._nodes[key]['tcp']:
- node['tcp']['passive_open_sessions'] = \
- self._nodes[key]['tcp']['passive_open_sessions']
+ if "active_open_sessions" in self._nodes[key]["tcp"]:
+ node["tcp"]["active_open_sessions"] = self._nodes[key]["tcp"][
+ "active_open_sessions"
+ ]
+ if "passive_open_sessions" in self._nodes[key]["tcp"]:
+ node["tcp"]["passive_open_sessions"] = self._nodes[key]["tcp"][
+ "passive_open_sessions"
+ ]
# Huge pages
- node['hugepages']['total'] = self._nodes[key]['hugepages']['total']
+ node["hugepages"]["total"] = self._nodes[key]["hugepages"]["total"]
# Write the auto config config file
- with open(self._autoconfig_filename, 'w') as yamlfile:
+ with open(self._autoconfig_filename, "w") as yamlfile:
yaml.dump(ydata, yamlfile)
def apply_huge_pages(self):
@@ -325,28 +337,28 @@ class AutoConfig(object):
"""
# Get main core
- cpu = '\n'
- if 'vpp_main_core' in node['cpu']:
- vpp_main_core = node['cpu']['vpp_main_core']
+ cpu = "\n"
+ if "vpp_main_core" in node["cpu"]:
+ vpp_main_core = node["cpu"]["vpp_main_core"]
else:
vpp_main_core = 0
if vpp_main_core != 0:
- cpu += ' main-core {}\n'.format(vpp_main_core)
+ cpu += " main-core {}\n".format(vpp_main_core)
# Get workers
- vpp_workers = node['cpu']['vpp_workers']
+ vpp_workers = node["cpu"]["vpp_workers"]
vpp_worker_len = len(vpp_workers)
if vpp_worker_len > 0:
- vpp_worker_str = ''
+ vpp_worker_str = ""
for i, worker in enumerate(vpp_workers):
if i > 0:
- vpp_worker_str += ','
+ vpp_worker_str += ","
if worker[0] == worker[1]:
vpp_worker_str += "{}".format(worker[0])
else:
vpp_worker_str += "{}-{}".format(worker[0], worker[1])
- cpu += ' corelist-workers {}\n'.format(vpp_worker_str)
+ cpu += " corelist-workers {}\n".format(vpp_worker_str)
return cpu
@@ -359,41 +371,41 @@ class AutoConfig(object):
:type node: dict
"""
- devices = ''
- ports_per_numa = node['cpu']['ports_per_numa']
+ devices = ""
+ ports_per_numa = node["cpu"]["ports_per_numa"]
for item in ports_per_numa.items():
value = item[1]
- interfaces = value['interfaces']
+ interfaces = value["interfaces"]
# if 0 was specified for the number of vpp workers, use 1 queue
num_rx_queues = None
num_tx_queues = None
- if 'rx_queues' in value:
- num_rx_queues = value['rx_queues']
- if 'tx_queues' in value:
- num_tx_queues = value['tx_queues']
+ if "rx_queues" in value:
+ num_rx_queues = value["rx_queues"]
+ if "tx_queues" in value:
+ num_tx_queues = value["tx_queues"]
num_rx_desc = None
num_tx_desc = None
# Create the devices string
for interface in interfaces:
- pci_address = interface['pci_address']
+ pci_address = interface["pci_address"]
pci_address = pci_address.lstrip("'").rstrip("'")
- devices += '\n'
- devices += ' dev {} {{ \n'.format(pci_address)
+ devices += "\n"
+ devices += " dev {} {{ \n".format(pci_address)
if num_rx_queues:
- devices += ' num-rx-queues {}\n'.format(num_rx_queues)
+ devices += " num-rx-queues {}\n".format(num_rx_queues)
else:
- devices += ' num-rx-queues {}\n'.format(1)
+ devices += " num-rx-queues {}\n".format(1)
if num_tx_queues:
- devices += ' num-tx-queues {}\n'.format(num_tx_queues)
+ devices += " num-tx-queues {}\n".format(num_tx_queues)
if num_rx_desc:
- devices += ' num-rx-desc {}\n'.format(num_rx_desc)
+ devices += " num-rx-desc {}\n".format(num_rx_desc)
if num_tx_desc:
- devices += ' num-tx-desc {}\n'.format(num_tx_desc)
- devices += ' }'
+ devices += " num-tx-desc {}\n".format(num_tx_desc)
+ devices += " }"
return devices
@@ -405,20 +417,25 @@ class AutoConfig(object):
:param node: Node dictionary with cpuinfo.
:type node: dict
"""
- buffers = ''
- total_mbufs = node['cpu']['total_mbufs']
+ buffers = ""
+ total_mbufs = node["cpu"]["total_mbufs"]
# If the total mbufs is not 0 or less than the default, set num-bufs
logging.debug("Total mbufs: {}".format(total_mbufs))
if total_mbufs != 0 and total_mbufs > 16384:
- buffers += ' buffers-per-numa {}'.format(total_mbufs)
+ buffers += " buffers-per-numa {}".format(total_mbufs)
return buffers
@staticmethod
- def _calc_vpp_workers(node, vpp_workers, numa_node, other_cpus_end,
- total_vpp_workers,
- reserve_vpp_main_core):
+ def _calc_vpp_workers(
+ node,
+ vpp_workers,
+ numa_node,
+ other_cpus_end,
+ total_vpp_workers,
+ reserve_vpp_main_core,
+ ):
"""
Calculate the VPP worker information
@@ -440,7 +457,7 @@ class AutoConfig(object):
"""
# Can we fit the workers in one of these slices
- cpus = node['cpu']['cpus_per_node'][numa_node]
+ cpus = node["cpu"]["cpus_per_node"][numa_node]
for cpu in cpus:
start = cpu[0]
end = cpu[1]
@@ -454,7 +471,7 @@ class AutoConfig(object):
if workers_end <= end:
if reserve_vpp_main_core:
- node['cpu']['vpp_main_core'] = start - 1
+ node["cpu"]["vpp_main_core"] = start - 1
reserve_vpp_main_core = False
if total_vpp_workers:
vpp_workers.append((start, workers_end))
@@ -462,15 +479,14 @@ class AutoConfig(object):
# We still need to reserve the main core
if reserve_vpp_main_core:
- node['cpu']['vpp_main_core'] = other_cpus_end + 1
+ node["cpu"]["vpp_main_core"] = other_cpus_end + 1
return reserve_vpp_main_core
@staticmethod
- def _calc_desc_and_queues(total_numa_nodes,
- total_ports_per_numa,
- total_rx_queues,
- ports_per_numa_value):
+ def _calc_desc_and_queues(
+ total_numa_nodes, total_ports_per_numa, total_rx_queues, ports_per_numa_value
+ ):
"""
Calculate the number of descriptors and queues
@@ -494,8 +510,10 @@ class AutoConfig(object):
# Get the descriptor entries
desc_entries = 1024
- ports_per_numa_value['rx_queues'] = rx_queues
- total_mbufs = ((rx_queues * desc_entries) + (tx_queues * desc_entries)) * total_ports_per_numa
+ ports_per_numa_value["rx_queues"] = rx_queues
+ total_mbufs = (
+ (rx_queues * desc_entries) + (tx_queues * desc_entries)
+ ) * total_ports_per_numa
return total_mbufs
@@ -515,12 +533,12 @@ class AutoConfig(object):
ports_per_numa = {}
for item in interfaces.items():
i = item[1]
- if i['numa_node'] not in ports_per_numa:
- ports_per_numa[i['numa_node']] = {'interfaces': []}
- ports_per_numa[i['numa_node']]['interfaces'].append(i)
+ if i["numa_node"] not in ports_per_numa:
+ ports_per_numa[i["numa_node"]] = {"interfaces": []}
+ ports_per_numa[i["numa_node"]]["interfaces"].append(i)
else:
- ports_per_numa[i['numa_node']]['interfaces'].append(i)
- node['cpu']['ports_per_numa'] = ports_per_numa
+ ports_per_numa[i["numa_node"]]["interfaces"].append(i)
+ node["cpu"]["ports_per_numa"] = ports_per_numa
return ports_per_numa
@@ -536,24 +554,24 @@ class AutoConfig(object):
node = i[1]
# get total number of nic ports
- interfaces = node['interfaces']
+ interfaces = node["interfaces"]
# Make a list of ports by numa node
ports_per_numa = self._create_ports_per_numa(node, interfaces)
# Get the number of cpus to skip, we never use the first cpu
other_cpus_start = 1
- other_cpus_end = other_cpus_start + node['cpu']['total_other_cpus'] - 1
+ other_cpus_end = other_cpus_start + node["cpu"]["total_other_cpus"] - 1
other_workers = None
if other_cpus_end != 0:
other_workers = (other_cpus_start, other_cpus_end)
- node['cpu']['other_workers'] = other_workers
+ node["cpu"]["other_workers"] = other_workers
# Allocate the VPP main core and workers
vpp_workers = []
- reserve_vpp_main_core = node['cpu']['reserve_vpp_main_core']
- total_vpp_cpus = node['cpu']['total_vpp_cpus']
- total_rx_queues = node['cpu']['total_rx_queues']
+ reserve_vpp_main_core = node["cpu"]["reserve_vpp_main_core"]
+ total_vpp_cpus = node["cpu"]["total_vpp_cpus"]
+ total_rx_queues = node["cpu"]["total_rx_queues"]
# If total_vpp_cpus is 0 or is less than the numa nodes with ports
# then we shouldn't get workers
@@ -572,14 +590,21 @@ class AutoConfig(object):
# Get the number of descriptors and queues
mbufs = self._calc_desc_and_queues(
len(ports_per_numa),
- len(value['interfaces']), total_rx_queues, value)
+ len(value["interfaces"]),
+ total_rx_queues,
+ value,
+ )
total_mbufs += mbufs
# Get the VPP workers
reserve_vpp_main_core = self._calc_vpp_workers(
- node, vpp_workers, numa_node,
- other_cpus_end, total_workers_node,
- reserve_vpp_main_core)
+ node,
+ vpp_workers,
+ numa_node,
+ other_cpus_end,
+ total_workers_node,
+ reserve_vpp_main_core,
+ )
total_mbufs *= 2.5
total_mbufs = int(total_mbufs)
@@ -587,8 +612,8 @@ class AutoConfig(object):
total_mbufs = 0
# Save the info
- node['cpu']['vpp_workers'] = vpp_workers
- node['cpu']['total_mbufs'] = total_mbufs
+ node["cpu"]["vpp_workers"] = vpp_workers
+ node["cpu"]["total_mbufs"] = total_mbufs
# Write the config
self.updateconfig()
@@ -602,54 +627,55 @@ class AutoConfig(object):
:type node: dict
"""
- active_open_sessions = node['tcp']['active_open_sessions']
+ active_open_sessions = node["tcp"]["active_open_sessions"]
aos = int(active_open_sessions)
- passive_open_sessions = node['tcp']['passive_open_sessions']
+ passive_open_sessions = node["tcp"]["passive_open_sessions"]
pos = int(passive_open_sessions)
# Generate the api-segment gid vpp sheit in any case
if (aos + pos) == 0:
- tcp = '\n'.join([
+ tcp = "\n".join(["api-segment {", " gid vpp", "}"])
+ return tcp.rstrip("\n")
+
+ tcp = "\n".join(
+ [
+ "# TCP stack-related configuration parameters",
+ "# expecting {:d} client sessions, {:d} server sessions\n".format(
+ aos, pos
+ ),
+ "heapsize 4g\n",
"api-segment {",
- " gid vpp",
- "}"
- ])
- return tcp.rstrip('\n')
-
- tcp = '\n'.join([
- "# TCP stack-related configuration parameters",
- "# expecting {:d} client sessions, {:d} server sessions\n".format(
- aos, pos),
- "heapsize 4g\n",
- "api-segment {",
- " global-size 2000M",
- " api-size 1G",
- "}\n",
-
- "session {",
- " event-queue-length {:d}".format(aos + pos),
- " preallocated-sessions {:d}".format(aos + pos),
- " v4-session-table-buckets {:d}".format((aos + pos) // 4),
- " v4-session-table-memory 3g\n"
- ])
+ " global-size 2000M",
+ " api-size 1G",
+ "}\n",
+ "session {",
+ " event-queue-length {:d}".format(aos + pos),
+ " preallocated-sessions {:d}".format(aos + pos),
+ " v4-session-table-buckets {:d}".format((aos + pos) // 4),
+ " v4-session-table-memory 3g\n",
+ ]
+ )
if aos > 0:
- tcp = tcp + " v4-halfopen-table-buckets {:d}".format(
- (aos + pos) // 4) + "\n"
+ tcp = (
+ tcp + " v4-halfopen-table-buckets {:d}".format((aos + pos) // 4) + "\n"
+ )
tcp = tcp + " v4-halfopen-table-memory 3g\n"
- tcp = tcp + " local-endpoints-table-buckets {:d}".format(
- (aos + pos) // 4) + "\n"
+ tcp = (
+ tcp
+ + " local-endpoints-table-buckets {:d}".format((aos + pos) // 4)
+ + "\n"
+ )
tcp = tcp + " local-endpoints-table-memory 3g\n"
tcp = tcp + "}\n\n"
tcp = tcp + "tcp {\n"
tcp = tcp + " preallocated-connections {:d}".format(aos + pos) + "\n"
if aos > 0:
- tcp = tcp + " preallocated-half-open-connections {:d}".format(
- aos) + "\n"
+ tcp = tcp + " preallocated-half-open-connections {:d}".format(aos) + "\n"
tcp = tcp + "}\n\n"
- return tcp.rstrip('\n')
+ return tcp.rstrip("\n")
def apply_vpp_startup(self):
"""
@@ -662,8 +688,8 @@ class AutoConfig(object):
node = i[1]
# Get the startup file
- rootdir = node['rootdir']
- sfile = rootdir + node['vpp']['startup_config_file']
+ rootdir = node["rootdir"]
+ sfile = rootdir + node["vpp"]["startup_config_file"]
# Get the buffers
devices = self._apply_vpp_devices(node)
@@ -680,27 +706,22 @@ class AutoConfig(object):
self._autoconfig_backup_file(sfile)
# Get the template
- tfile = sfile + '.template'
- (ret, stdout, stderr) = \
- VPPUtil.exec_command('cat {}'.format(tfile))
+ tfile = sfile + ".template"
+ (ret, stdout, stderr) = VPPUtil.exec_command("cat {}".format(tfile))
if ret != 0:
- raise RuntimeError('Executing cat command failed to node {}'.
- format(node['host']))
- startup = stdout.format(cpu=cpu,
- buffers=buffers,
- devices=devices,
- tcp=tcp)
-
- (ret, stdout, stderr) = \
- VPPUtil.exec_command('rm {}'.format(sfile))
+ raise RuntimeError(
+ "Executing cat command failed to node {}".format(node["host"])
+ )
+ startup = stdout.format(cpu=cpu, buffers=buffers, devices=devices, tcp=tcp)
+
+ (ret, stdout, stderr) = VPPUtil.exec_command("rm {}".format(sfile))
if ret != 0:
logging.debug(stderr)
cmd = "sudo cat > {0} << EOF\n{1}\n".format(sfile, startup)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('Writing config failed node {}'.
- format(node['host']))
+ raise RuntimeError("Writing config failed node {}".format(node["host"]))
def apply_grub_cmdline(self):
"""
@@ -712,10 +733,10 @@ class AutoConfig(object):
node = i[1]
# Get the isolated CPUs
- other_workers = node['cpu']['other_workers']
- vpp_workers = node['cpu']['vpp_workers']
- if 'vpp_main_core' in node['cpu']:
- vpp_main_core = node['cpu']['vpp_main_core']
+ other_workers = node["cpu"]["other_workers"]
+ vpp_workers = node["cpu"]["vpp_workers"]
+ if "vpp_main_core" in node["cpu"]:
+ vpp_main_core = node["cpu"]["vpp_main_core"]
else:
vpp_main_core = 0
all_workers = []
@@ -724,12 +745,12 @@ class AutoConfig(object):
if vpp_main_core != 0:
all_workers += [(vpp_main_core, vpp_main_core)]
all_workers += vpp_workers
- isolated_cpus = ''
+ isolated_cpus = ""
for idx, worker in enumerate(all_workers):
if worker is None:
continue
if idx > 0:
- isolated_cpus += ','
+ isolated_cpus += ","
if worker[0] == worker[1]:
isolated_cpus += "{}".format(worker[0])
else:
@@ -737,11 +758,10 @@ class AutoConfig(object):
vppgrb = VppGrubUtil(node)
current_cmdline = vppgrb.get_current_cmdline()
- if 'grub' not in node:
- node['grub'] = {}
- node['grub']['current_cmdline'] = current_cmdline
- node['grub']['default_cmdline'] = \
- vppgrb.apply_cmdline(node, isolated_cpus)
+ if "grub" not in node:
+ node["grub"] = {}
+ node["grub"]["current_cmdline"] = current_cmdline
+ node["grub"]["default_cmdline"] = vppgrb.apply_cmdline(node, isolated_cpus)
self.updateconfig()
@@ -756,14 +776,14 @@ class AutoConfig(object):
hpg = VppHugePageUtil(node)
max_map_count, shmmax = hpg.get_huge_page_config()
- node['hugepages']['max_map_count'] = max_map_count
- node['hugepages']['shmax'] = shmmax
+ node["hugepages"]["max_map_count"] = max_map_count
+ node["hugepages"]["shmax"] = shmmax
total, free, size, memtotal, memfree = hpg.get_actual_huge_pages()
- node['hugepages']['actual_total'] = total
- node['hugepages']['free'] = free
- node['hugepages']['size'] = size
- node['hugepages']['memtotal'] = memtotal
- node['hugepages']['memfree'] = memfree
+ node["hugepages"]["actual_total"] = total
+ node["hugepages"]["free"] = free
+ node["hugepages"]["size"] = size
+ node["hugepages"]["memtotal"] = memtotal
+ node["hugepages"]["memfree"] = memfree
self.updateconfig()
@@ -782,14 +802,14 @@ class AutoConfig(object):
# Get the total number of isolated CPUs
current_iso_cpus = 0
- iso_cpur = re.findall(r'isolcpus=[\w+\-,]+', current_cmdline)
+ iso_cpur = re.findall(r"isolcpus=[\w+\-,]+", current_cmdline)
iso_cpurl = len(iso_cpur)
if iso_cpurl > 0:
iso_cpu_str = iso_cpur[0]
- iso_cpu_str = iso_cpu_str.split('=')[1]
- iso_cpul = iso_cpu_str.split(',')
+ iso_cpu_str = iso_cpu_str.split("=")[1]
+ iso_cpul = iso_cpu_str.split(",")
for iso_cpu in iso_cpul:
- isocpuspl = iso_cpu.split('-')
+ isocpuspl = iso_cpu.split("-")
if len(isocpuspl) == 1:
current_iso_cpus += 1
else:
@@ -800,11 +820,11 @@ class AutoConfig(object):
else:
current_iso_cpus += second - first
- if 'grub' not in node:
- node['grub'] = {}
- node['grub']['current_cmdline'] = current_cmdline
- node['grub']['default_cmdline'] = default_cmdline
- node['grub']['current_iso_cpus'] = current_iso_cpus
+ if "grub" not in node:
+ node["grub"] = {}
+ node["grub"]["current_cmdline"] = current_cmdline
+ node["grub"]["default_cmdline"] = default_cmdline
+ node["grub"]["current_iso_cpus"] = current_iso_cpus
self.updateconfig()
@@ -822,11 +842,11 @@ class AutoConfig(object):
vpp.get_all_devices()
# Save the device information
- node['devices'] = {}
- node['devices']['dpdk_devices'] = vpp.get_dpdk_devices()
- node['devices']['kernel_devices'] = vpp.get_kernel_devices()
- node['devices']['other_devices'] = vpp.get_other_devices()
- node['devices']['linkup_devices'] = vpp.get_link_up_devices()
+ node["devices"] = {}
+ node["devices"]["dpdk_devices"] = vpp.get_dpdk_devices()
+ node["devices"]["kernel_devices"] = vpp.get_kernel_devices()
+ node["devices"]["other_devices"] = vpp.get_other_devices()
+ node["devices"]["linkup_devices"] = vpp.get_link_up_devices()
def get_devices_per_node(self):
"""
@@ -856,20 +876,25 @@ class AutoConfig(object):
:rtype: list
"""
- cmd = 'lscpu -p'
+ cmd = "lscpu -p"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.
- format(cmd, node['host'], stderr))
+ raise RuntimeError(
+ "{} failed on node {} {}".format(cmd, node["host"], stderr)
+ )
pcpus = []
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
for line in lines:
- if line == '' or line[0] == '#':
+ if line == "" or line[0] == "#":
continue
- linesplit = line.split(',')
- layout = {'cpu': linesplit[0], 'core': linesplit[1],
- 'socket': linesplit[2], 'node': linesplit[3]}
+ linesplit = line.split(",")
+ layout = {
+ "cpu": linesplit[0],
+ "core": linesplit[1],
+ "socket": linesplit[2],
+ "node": linesplit[3],
+ }
# cpu, core, socket, node
pcpus.append(layout)
@@ -890,14 +915,14 @@ class AutoConfig(object):
# Get the cpu layout
layout = self.get_cpu_layout(node)
- node['cpu']['layout'] = layout
+ node["cpu"]["layout"] = layout
- cpuinfo = node['cpuinfo']
+ cpuinfo = node["cpuinfo"]
smt_enabled = CpuUtils.is_smt_enabled(cpuinfo)
- node['cpu']['smt_enabled'] = smt_enabled
+ node["cpu"]["smt_enabled"] = smt_enabled
# We don't want to write the cpuinfo
- node['cpuinfo'] = ""
+ node["cpuinfo"] = ""
# Write the config
self.updateconfig()
@@ -932,46 +957,59 @@ class AutoConfig(object):
:type numa_nodes: list
"""
- print("\nYour system has {} core(s) and {} Numa Nodes.".
- format(total_cpus, len(numa_nodes)))
- print("To begin, we suggest not reserving any cores for "
- "VPP or other processes.")
- print("Then to improve performance start reserving cores and "
- "adding queues as needed.")
+ print(
+ "\nYour system has {} core(s) and {} Numa Nodes.".format(
+ total_cpus, len(numa_nodes)
+ )
+ )
+ print(
+ "To begin, we suggest not reserving any cores for "
+ "VPP or other processes."
+ )
+ print(
+ "Then to improve performance start reserving cores and "
+ "adding queues as needed."
+ )
# Leave 1 for the general system
total_cpus -= 1
max_vpp_cpus = min(total_cpus, 4)
total_vpp_cpus = 0
if max_vpp_cpus > 0:
- question = "\nHow many core(s) shall we reserve for " \
- "VPP [0-{}][0]? ".format(max_vpp_cpus)
+ question = (
+ "\nHow many core(s) shall we reserve for "
+ "VPP [0-{}][0]? ".format(max_vpp_cpus)
+ )
total_vpp_cpus = self._ask_user_range(question, 0, max_vpp_cpus, 0)
- node['cpu']['total_vpp_cpus'] = total_vpp_cpus
+ node["cpu"]["total_vpp_cpus"] = total_vpp_cpus
total_other_cpus = 0
max_other_cores = total_cpus - total_vpp_cpus
if max_other_cores > 0:
- question = 'How many core(s) do you want to reserve for ' \
- 'processes other than VPP? [0-{}][0]? '. format(str(max_other_cores))
+ question = (
+ "How many core(s) do you want to reserve for "
+ "processes other than VPP? [0-{}][0]? ".format(str(max_other_cores))
+ )
total_other_cpus = self._ask_user_range(question, 0, max_other_cores, 0)
- node['cpu']['total_other_cpus'] = total_other_cpus
+ node["cpu"]["total_other_cpus"] = total_other_cpus
max_main_cpus = total_cpus - total_vpp_cpus - total_other_cpus
reserve_vpp_main_core = False
if max_main_cpus > 0:
question = "Should we reserve 1 core for the VPP Main thread? "
question += "[y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
reserve_vpp_main_core = True
- node['cpu']['reserve_vpp_main_core'] = reserve_vpp_main_core
- node['cpu']['vpp_main_core'] = 0
+ node["cpu"]["reserve_vpp_main_core"] = reserve_vpp_main_core
+ node["cpu"]["vpp_main_core"] = 0
- question = "How many RX queues per port shall we use for " \
- "VPP [1-4][1]? ".format(max_vpp_cpus)
+ question = (
+ "How many RX queues per port shall we use for "
+ "VPP [1-4][1]? ".format(max_vpp_cpus)
+ )
total_rx_queues = self._ask_user_range(question, 1, 4, 1)
- node['cpu']['total_rx_queues'] = total_rx_queues
+ node["cpu"]["total_rx_queues"] = total_rx_queues
def modify_cpu(self, ask_questions=True):
"""
@@ -995,50 +1033,50 @@ class AutoConfig(object):
# Assume the number of cpus per slice is always the same as the
# first slice
- first_node = '0'
+ first_node = "0"
for cpu in cpu_layout:
- if cpu['node'] != first_node:
+ if cpu["node"] != first_node:
break
total_cpus_per_slice += 1
# Get the total number of cpus, cores, and numa nodes from the
# cpu layout
for cpul in cpu_layout:
- numa_node = cpul['node']
- core = cpul['core']
- cpu = cpul['cpu']
+ numa_node = cpul["node"]
+ core = cpul["core"]
+ cpu = cpul["cpu"]
total_cpus += 1
if numa_node not in cpus_per_node:
cpus_per_node[numa_node] = []
cpuperslice = int(cpu) % total_cpus_per_slice
if cpuperslice == 0:
- cpus_per_node[numa_node].append((int(cpu), int(cpu) +
- total_cpus_per_slice - 1))
+ cpus_per_node[numa_node].append(
+ (int(cpu), int(cpu) + total_cpus_per_slice - 1)
+ )
if numa_node not in numa_nodes:
numa_nodes.append(numa_node)
if core not in cores:
cores.append(core)
- node['cpu']['cpus_per_node'] = cpus_per_node
+ node["cpu"]["cpus_per_node"] = cpus_per_node
# Ask the user some questions
if ask_questions and total_cpus >= 4:
self._modify_cpu_questions(node, total_cpus, numa_nodes)
# Populate the interfaces with the numa node
- if 'interfaces' in node:
- ikeys = node['interfaces'].keys()
+ if "interfaces" in node:
+ ikeys = node["interfaces"].keys()
VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys))
# We don't want to write the cpuinfo
- node['cpuinfo'] = ""
+ node["cpuinfo"] = ""
# Write the configs
self._update_auto_config()
self.updateconfig()
- def _modify_other_devices(self, node,
- other_devices, kernel_devices, dpdk_devices):
+ def _modify_other_devices(self, node, other_devices, kernel_devices, dpdk_devices):
"""
Modify the devices configuration, asking for the user for the values.
@@ -1046,31 +1084,31 @@ class AutoConfig(object):
odevices_len = len(other_devices)
if odevices_len > 0:
- print("\nThese device(s) are currently NOT being used "
- "by VPP or the OS.\n")
+ print(
+ "\nThese device(s) are currently NOT being used " "by VPP or the OS.\n"
+ )
VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
question = "\nWould you like to give any of these devices"
question += " back to the OS [Y/n]? "
- answer = self._ask_user_yn(question, 'Y')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "Y")
+ if answer == "y":
vppd = {}
for dit in other_devices.items():
dvid = dit[0]
device = dit[1]
- question = "Would you like to use device {} for". \
- format(dvid)
+ question = "Would you like to use device {} for".format(dvid)
question += " the OS [y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
- if 'unused' in device and len(
- device['unused']) != 0 and \
- device['unused'][0] != '':
- driver = device['unused'][0]
- ret = VppPCIUtil.bind_vpp_device(
- node, driver, dvid)
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
+ if (
+ "unused" in device
+ and len(device["unused"]) != 0
+ and device["unused"][0] != ""
+ ):
+ driver = device["unused"][0]
+ ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
if ret:
- logging.debug(
- 'Could not bind device {}'.format(dvid))
+ logging.debug("Could not bind device {}".format(dvid))
else:
vppd[dvid] = device
for dit in vppd.items():
@@ -1081,34 +1119,35 @@ class AutoConfig(object):
odevices_len = len(other_devices)
if odevices_len > 0:
- print("\nThese device(s) are still NOT being used "
- "by VPP or the OS.\n")
+ print("\nThese device(s) are still NOT being used " "by VPP or the OS.\n")
VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
question = "\nWould you like use any of these for VPP [y/N]? "
- answer = self._ask_user_yn(question, 'N')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "N")
+ if answer == "y":
vppd = {}
for dit in other_devices.items():
dvid = dit[0]
device = dit[1]
question = "Would you like to use device {} ".format(dvid)
question += "for VPP [y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
vppd[dvid] = device
for dit in vppd.items():
dvid = dit[0]
device = dit[1]
- if 'unused' in device and len(device['unused']) != 0 and \
- device['unused'][0] != '':
- driver = device['unused'][0]
+ if (
+ "unused" in device
+ and len(device["unused"]) != 0
+ and device["unused"][0] != ""
+ ):
+ driver = device["unused"][0]
logging.debug(
- 'Binding device {} to driver {}'.format(dvid,
- driver))
+ "Binding device {} to driver {}".format(dvid, driver)
+ )
ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
if ret:
- logging.debug(
- 'Could not bind device {}'.format(dvid))
+ logging.debug("Could not bind device {}".format(dvid))
else:
dpdk_devices[dvid] = device
del other_devices[dvid]
@@ -1121,22 +1160,23 @@ class AutoConfig(object):
for i in self._nodes.items():
node = i[1]
- devices = node['devices']
- all_devices = devices['other_devices']
- all_devices.update(devices['dpdk_devices'])
- all_devices.update(devices['kernel_devices'])
+ devices = node["devices"]
+ all_devices = devices["other_devices"]
+ all_devices.update(devices["dpdk_devices"])
+ all_devices.update(devices["kernel_devices"])
current_ifcs = {}
interfaces = {}
- if 'interfaces' in node:
- current_ifcs = node['interfaces']
+ if "interfaces" in node:
+ current_ifcs = node["interfaces"]
if current_ifcs:
for ifc in current_ifcs.values():
- dvid = ifc['pci_address']
+ dvid = ifc["pci_address"]
if dvid in all_devices:
- VppPCIUtil.vpp_create_interface(interfaces, dvid,
- all_devices[dvid])
- node['interfaces'] = interfaces
+ VppPCIUtil.vpp_create_interface(
+ interfaces, dvid, all_devices[dvid]
+ )
+ node["interfaces"] = interfaces
self.updateconfig()
@@ -1148,86 +1188,98 @@ class AutoConfig(object):
for i in self._nodes.items():
node = i[1]
- devices = node['devices']
- other_devices = devices['other_devices']
- kernel_devices = devices['kernel_devices']
- dpdk_devices = devices['dpdk_devices']
+ devices = node["devices"]
+ other_devices = devices["other_devices"]
+ kernel_devices = devices["kernel_devices"]
+ dpdk_devices = devices["dpdk_devices"]
if other_devices:
- self._modify_other_devices(node, other_devices,
- kernel_devices, dpdk_devices)
+ self._modify_other_devices(
+ node, other_devices, kernel_devices, dpdk_devices
+ )
# Get the devices again for this node
self._get_device(node)
- devices = node['devices']
- kernel_devices = devices['kernel_devices']
- dpdk_devices = devices['dpdk_devices']
+ devices = node["devices"]
+ kernel_devices = devices["kernel_devices"]
+ dpdk_devices = devices["dpdk_devices"]
klen = len(kernel_devices)
if klen > 0:
print("\nThese devices are safe to be used with VPP.\n")
VppPCIUtil.show_vpp_devices(kernel_devices)
- question = "\nWould you like to use any of these " \
- "device(s) for VPP [y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
+ question = (
+ "\nWould you like to use any of these " "device(s) for VPP [y/N]? "
+ )
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
vppd = {}
for dit in kernel_devices.items():
dvid = dit[0]
device = dit[1]
question = "Would you like to use device {} ".format(dvid)
question += "for VPP [y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
vppd[dvid] = device
for dit in vppd.items():
dvid = dit[0]
device = dit[1]
- if 'unused' in device and len(
- device['unused']) != 0 and device['unused'][0] != '':
- driver = device['unused'][0]
- question = "Would you like to bind the driver {} for {} [y/N]? ".format(driver, dvid)
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
- logging.debug('Binding device {} to driver {}'.format(dvid, driver))
+ if (
+ "unused" in device
+ and len(device["unused"]) != 0
+ and device["unused"][0] != ""
+ ):
+ driver = device["unused"][0]
+ question = "Would you like to bind the driver {} for {} [y/N]? ".format(
+ driver, dvid
+ )
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
+ logging.debug(
+ "Binding device {} to driver {}".format(
+ dvid, driver
+ )
+ )
ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
if ret:
- logging.debug('Could not bind device {}'.format(dvid))
+ logging.debug(
+ "Could not bind device {}".format(dvid)
+ )
dpdk_devices[dvid] = device
del kernel_devices[dvid]
dlen = len(dpdk_devices)
if dlen > 0:
print("\nThese device(s) are already using DPDK.\n")
- VppPCIUtil.show_vpp_devices(dpdk_devices,
- show_interfaces=False)
+ VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False)
question = "\nWould you like to remove any of "
question += "these device(s) [y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
vppdl = {}
for dit in dpdk_devices.items():
dvid = dit[0]
device = dit[1]
- question = "Would you like to remove {} [y/N]? ". \
- format(dvid)
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
+ question = "Would you like to remove {} [y/N]? ".format(dvid)
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
vppdl[dvid] = device
for dit in vppdl.items():
dvid = dit[0]
device = dit[1]
- if 'unused' in device and len(
- device['unused']) != 0 and device['unused'][0] != '':
- driver = device['unused'][0]
+ if (
+ "unused" in device
+ and len(device["unused"]) != 0
+ and device["unused"][0] != ""
+ ):
+ driver = device["unused"][0]
logging.debug(
- 'Binding device {} to driver {}'.format(
- dvid, driver))
- ret = VppPCIUtil.bind_vpp_device(node, driver,
- dvid)
+ "Binding device {} to driver {}".format(dvid, driver)
+ )
+ ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
if ret:
- logging.debug(
- 'Could not bind device {}'.format(dvid))
+ logging.debug("Could not bind device {}".format(dvid))
else:
kernel_devices[dvid] = device
del dpdk_devices[dvid]
@@ -1237,7 +1289,7 @@ class AutoConfig(object):
dvid = dit[0]
device = dit[1]
VppPCIUtil.vpp_create_interface(interfaces, dvid, device)
- node['interfaces'] = interfaces
+ node["interfaces"] = interfaces
self._update_auto_config()
self.updateconfig()
@@ -1251,29 +1303,27 @@ class AutoConfig(object):
for i in self._nodes.items():
node = i[1]
- total = node['hugepages']['actual_total']
- free = node['hugepages']['free']
- size = node['hugepages']['size']
- memfree = node['hugepages']['memfree'].split(' ')[0]
- hugesize = int(size.split(' ')[0])
+ total = node["hugepages"]["actual_total"]
+ free = node["hugepages"]["free"]
+ size = node["hugepages"]["size"]
+ memfree = node["hugepages"]["memfree"].split(" ")[0]
+ hugesize = int(size.split(" ")[0])
# The max number of huge pages should be no more than
# 70% of total free memory
maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES // 100) // hugesize
- print("\nThere currently {} {} huge pages free.".format(
- free, size))
- question = "Do you want to reconfigure the number of " \
- "huge pages [y/N]? "
- answer = self._ask_user_yn(question, 'n')
- if answer == 'n':
- node['hugepages']['total'] = total
+ print("\nThere currently {} {} huge pages free.".format(free, size))
+ question = "Do you want to reconfigure the number of " "huge pages [y/N]? "
+ answer = self._ask_user_yn(question, "n")
+ if answer == "n":
+ node["hugepages"]["total"] = total
continue
- print("\nThere currently a total of {} huge pages.".
- format(total))
- question = "How many huge pages do you want [{} - {}][{}]? ". \
- format(MIN_TOTAL_HUGE_PAGES, maxpages, MIN_TOTAL_HUGE_PAGES)
+ print("\nThere currently a total of {} huge pages.".format(total))
+ question = "How many huge pages do you want [{} - {}][{}]? ".format(
+ MIN_TOTAL_HUGE_PAGES, maxpages, MIN_TOTAL_HUGE_PAGES
+ )
answer = self._ask_user_range(question, 1024, maxpages, 1024)
- node['hugepages']['total'] = str(answer)
+ node["hugepages"]["total"] = str(answer)
# Update auto-config.yaml
self._update_auto_config()
@@ -1298,21 +1348,25 @@ class AutoConfig(object):
for i in self._nodes.items():
node = i[1]
- question = "\nHow many active-open / tcp client sessions are " \
- "expected [0-10000000][0]? "
+ question = (
+ "\nHow many active-open / tcp client sessions are "
+ "expected [0-10000000][0]? "
+ )
answer = self._ask_user_range(question, 0, 10000000, 0)
# Less than 10K is equivalent to 0
if int(answer) < 10000:
answer = 0
- node['tcp']['active_open_sessions'] = answer
+ node["tcp"]["active_open_sessions"] = answer
- question = "How many passive-open / tcp server sessions are " \
- "expected [0-10000000][0]? "
+ question = (
+ "How many passive-open / tcp server sessions are "
+ "expected [0-10000000][0]? "
+ )
answer = self._ask_user_range(question, 0, 10000000, 0)
# Less than 10K is equivalent to 0
if int(answer) < 10000:
answer = 0
- node['tcp']['passive_open_sessions'] = answer
+ node["tcp"]["passive_open_sessions"] = answer
# Update auto-config.yaml
self._update_auto_config()
@@ -1329,7 +1383,7 @@ class AutoConfig(object):
:type node: dict
"""
- print('\nWe are patching the node "{}":\n'.format(node['host']))
+ print('\nWe are patching the node "{}":\n'.format(node["host"]))
QemuUtils.build_qemu(node, force_install=True, apply_patch=True)
@staticmethod
@@ -1341,44 +1395,44 @@ class AutoConfig(object):
cpu = CpuUtils.get_cpu_info_per_node(node)
- item = 'Model name'
+ item = "Model name"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- item = 'CPU(s)'
+ item = "CPU(s)"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- item = 'Thread(s) per core'
+ item = "Thread(s) per core"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- item = 'Core(s) per socket'
+ item = "Core(s) per socket"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- item = 'Socket(s)'
+ item = "Socket(s)"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- item = 'NUMA node(s)'
+ item = "NUMA node(s)"
numa_nodes = 0
if item in cpu:
numa_nodes = int(cpu[item])
for i in range(0, numa_nodes):
item = "NUMA node{} CPU(s)".format(i)
print("{:>20}: {}".format(item, cpu[item]))
- item = 'CPU max MHz'
+ item = "CPU max MHz"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- item = 'CPU min MHz'
+ item = "CPU min MHz"
if item in cpu:
print("{:>20}: {}".format(item, cpu[item]))
- if node['cpu']['smt_enabled']:
- smt = 'Enabled'
+ if node["cpu"]["smt_enabled"]:
+ smt = "Enabled"
else:
- smt = 'Disabled'
- print("{:>20}: {}".format('SMT', smt))
+ smt = "Disabled"
+ print("{:>20}: {}".format("SMT", smt))
# VPP Threads
print("\nVPP Threads: (Name: Cpu Number)")
- vpp_processes = cpu['vpp_processes']
+ vpp_processes = cpu["vpp_processes"]
for i in vpp_processes.items():
print(" {:10}: {:4}".format(i[0], i[1]))
@@ -1389,8 +1443,8 @@ class AutoConfig(object):
"""
- if 'cpu' in node and 'total_mbufs' in node['cpu']:
- total_mbufs = node['cpu']['total_mbufs']
+ if "cpu" in node and "total_mbufs" in node["cpu"]:
+ total_mbufs = node["cpu"]["total_mbufs"]
if total_mbufs != 0:
print("Total Number of Buffers: {}".format(total_mbufs))
@@ -1412,16 +1466,14 @@ class AutoConfig(object):
dpdk_devs = vpp.get_dpdk_devices()
if len(dpdk_devs):
print("\nDevices bound to DPDK drivers:")
- vpp.show_vpp_devices(dpdk_devs, show_interfaces=True,
- show_header=False)
+ vpp.show_vpp_devices(dpdk_devs, show_interfaces=True, show_header=False)
else:
print("\nNo devices bound to DPDK drivers")
other_devs = vpp.get_other_devices()
if len(other_devs):
print("\nDevices not bound to Kernel or DPDK drivers:")
- vpp.show_vpp_devices(other_devs, show_interfaces=True,
- show_header=False)
+ vpp.show_vpp_devices(other_devs, show_interfaces=True, show_header=False)
else:
print("\nNo devices not bound to Kernel or DPDK drivers")
@@ -1436,28 +1488,33 @@ class AutoConfig(object):
print("None")
return
- print("{:30} {:4} {:4} {:7} {:4} {:7}".
- format('Name', 'Numa', 'RXQs',
- 'RXDescs', 'TXQs', 'TXDescs'))
+ print(
+ "{:30} {:4} {:4} {:7} {:4} {:7}".format(
+ "Name", "Numa", "RXQs", "RXDescs", "TXQs", "TXDescs"
+ )
+ )
for intf in sorted(interfaces.items()):
name = intf[0]
value = intf[1]
- if name == 'local0':
+ if name == "local0":
continue
- numa = rx_qs = rx_ds = tx_qs = tx_ds = ''
- if 'numa' in value:
- numa = int(value['numa'])
- if 'rx queues' in value:
- rx_qs = int(value['rx queues'])
- if 'rx descs' in value:
- rx_ds = int(value['rx descs'])
- if 'tx queues' in value:
- tx_qs = int(value['tx queues'])
- if 'tx descs' in value:
- tx_ds = int(value['tx descs'])
-
- print("{:30} {:>4} {:>4} {:>7} {:>4} {:>7}".
- format(name, numa, rx_qs, rx_ds, tx_qs, tx_ds))
+ numa = rx_qs = rx_ds = tx_qs = tx_ds = ""
+ if "numa" in value:
+ numa = int(value["numa"])
+ if "rx queues" in value:
+ rx_qs = int(value["rx queues"])
+ if "rx descs" in value:
+ rx_ds = int(value["rx descs"])
+ if "tx queues" in value:
+ tx_qs = int(value["tx queues"])
+ if "tx descs" in value:
+ tx_ds = int(value["tx descs"])
+
+ print(
+ "{:30} {:>4} {:>4} {:>7} {:>4} {:>7}".format(
+ name, numa, rx_qs, rx_ds, tx_qs, tx_ds
+ )
+ )
@staticmethod
def hugepage_info(node):
@@ -1476,7 +1533,7 @@ class AutoConfig(object):
:returns: boolean
"""
- if 'interfaces' in node and len(node['interfaces']):
+ if "interfaces" in node and len(node["interfaces"]):
return True
else:
return False
@@ -1493,30 +1550,33 @@ class AutoConfig(object):
min_sys_res = True
# CPUs
- if 'layout' in node['cpu']:
- total_cpus = len(node['cpu']['layout'])
+ if "layout" in node["cpu"]:
+ total_cpus = len(node["cpu"]["layout"])
if total_cpus < 2:
- print("\nThere is only {} CPU(s) available on this system. "
- "This is not enough to run VPP.".format(total_cpus))
+ print(
+ "\nThere is only {} CPU(s) available on this system. "
+ "This is not enough to run VPP.".format(total_cpus)
+ )
min_sys_res = False
# System Memory
- if 'free' in node['hugepages'] and \
- 'memfree' in node['hugepages'] and \
- 'size' in node['hugepages']:
- free = node['hugepages']['free']
- memfree = float(node['hugepages']['memfree'].split(' ')[0])
- hugesize = float(node['hugepages']['size'].split(' ')[0])
+ if (
+ "free" in node["hugepages"]
+ and "memfree" in node["hugepages"]
+ and "size" in node["hugepages"]
+ ):
+ free = node["hugepages"]["free"]
+ memfree = float(node["hugepages"]["memfree"].split(" ")[0])
+ hugesize = float(node["hugepages"]["size"].split(" ")[0])
memhugepages = MIN_TOTAL_HUGE_PAGES * hugesize
percentmemhugepages = (memhugepages / memfree) * 100
- if free is '0' and \
- percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES:
+ if free is "0" and percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES:
print(
"\nThe System has only {} of free memory. You will not "
"be able to allocate enough Huge Pages for VPP.".format(
- int(
- memfree))
+ int(memfree)
+ )
)
min_sys_res = False
@@ -1541,11 +1601,9 @@ class AutoConfig(object):
# Grub
print("\nGrub Command Line:")
- if 'grub' in node:
- print(" Current: {}".format(
- node['grub']['current_cmdline']))
- print(" Configured: {}".format(
- node['grub']['default_cmdline']))
+ if "grub" in node:
+ print(" Current: {}".format(node["grub"]["current_cmdline"]))
+ print(" Configured: {}".format(node["grub"]["default_cmdline"]))
# Huge Pages
print("\nHuge Pages:")
@@ -1586,17 +1644,18 @@ class AutoConfig(object):
interfaces_with_ip = []
for intf in sorted(interfaces.items()):
name = intf[0]
- if name == 'local0':
+ if name == "local0":
continue
- question = "Would you like add address to " \
- "interface {} [Y/n]? ".format(name)
- answer = self._ask_user_yn(question, 'y')
- if answer == 'y':
+ question = "Would you like add address to " "interface {} [Y/n]? ".format(
+ name
+ )
+ answer = self._ask_user_yn(question, "y")
+ if answer == "y":
address = {}
addr = self._ask_user_ipv4()
- address['name'] = name
- address['addr'] = addr
+ address["name"] = name
+ address["addr"] = addr
interfaces_with_ip.append(address)
return interfaces_with_ip
@@ -1618,40 +1677,37 @@ class AutoConfig(object):
for items in sorted(current_ints.items()):
name = items[0]
value = items[1]
- if 'address' not in value:
- address = 'Not Set'
+ if "address" not in value:
+ address = "Not Set"
else:
- address = value['address']
- print("{:30} {:20} {:10}".format(name, address,
- value['state']))
- question = "\nWould you like to keep this configuration " \
- "[Y/n]? "
- answer = self._ask_user_yn(question, 'y')
- if answer == 'y':
+ address = value["address"]
+ print("{:30} {:20} {:10}".format(name, address, value["state"]))
+ question = "\nWould you like to keep this configuration " "[Y/n]? "
+ answer = self._ask_user_yn(question, "y")
+ if answer == "y":
continue
else:
- print("\nThere are currently no interfaces with IP "
- "addresses.")
+ print("\nThere are currently no interfaces with IP " "addresses.")
# Create a script that add the ip addresses to the interfaces
# and brings the interfaces up
ints_with_addrs = self._ipv4_interface_setup_questions(node)
- content = ''
+ content = ""
for ints in ints_with_addrs:
- name = ints['name']
- addr = ints['addr']
- setipstr = 'set int ip address {} {}\n'.format(name, addr)
- setintupstr = 'set int state {} up\n'.format(name)
+ name = ints["name"]
+ addr = ints["addr"]
+ setipstr = "set int ip address {} {}\n".format(name, addr)
+ setintupstr = "set int state {} up\n".format(name)
content += setipstr + setintupstr
# Write the content to the script
- rootdir = node['rootdir']
- filename = rootdir + '/vpp/vpp-config/scripts/set_int_ipv4_and_up'
- with open(filename, 'w+') as sfile:
+ rootdir = node["rootdir"]
+ filename = rootdir + "/vpp/vpp-config/scripts/set_int_ipv4_and_up"
+ with open(filename, "w+") as sfile:
sfile.write(content)
# Execute the script
- cmd = 'vppctl exec {}'.format(filename)
+ cmd = "vppctl exec {}".format(filename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
@@ -1679,12 +1735,13 @@ class AutoConfig(object):
# First delete all the Virtual interfaces
for intf in sorted(interfaces.items()):
name = intf[0]
- if name[:7] == 'Virtual':
- cmd = 'vppctl delete vhost-user {}'.format(name)
+ if name[:7] == "Virtual":
+ cmd = "vppctl delete vhost-user {}".format(name)
(ret, stdout, stderr) = vpputl.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd, node['host'], stderr))
+ logging.debug(
+ "{} failed on node {} {}".format(cmd, node["host"], stderr)
+ )
# Create a virtual interface, for each interface the user wants to use
interfaces = vpputl.get_hardware(node)
@@ -1694,36 +1751,38 @@ class AutoConfig(object):
inum = 1
for intf in sorted(interfaces.items()):
name = intf[0]
- if name == 'local0':
+ if name == "local0":
continue
- question = "Would you like connect this interface {} to " \
- "the VM [Y/n]? ".format(name)
- answer = self._ask_user_yn(question, 'y')
- if answer == 'y':
- sockfilename = '/var/run/vpp/{}.sock'.format(
- name.replace('/', '_'))
+ question = (
+ "Would you like connect this interface {} to "
+ "the VM [Y/n]? ".format(name)
+ )
+ answer = self._ask_user_yn(question, "y")
+ if answer == "y":
+ sockfilename = "/var/run/vpp/{}.sock".format(name.replace("/", "_"))
if os.path.exists(sockfilename):
os.remove(sockfilename)
- cmd = 'vppctl create vhost-user socket {} server'.format(
- sockfilename)
+ cmd = "vppctl create vhost-user socket {} server".format(sockfilename)
(ret, stdout, stderr) = vpputl.exec_command(cmd)
if ret != 0:
raise RuntimeError(
- "Couldn't execute the command {}, {}.".format(cmd,
- stderr))
- vintname = stdout.rstrip('\r\n')
+ "Couldn't execute the command {}, {}.".format(cmd, stderr)
+ )
+ vintname = stdout.rstrip("\r\n")
- cmd = 'chmod 777 {}'.format(sockfilename)
+ cmd = "chmod 777 {}".format(sockfilename)
(ret, stdout, stderr) = vpputl.exec_command(cmd)
if ret != 0:
raise RuntimeError(
- "Couldn't execute the command {}, {}.".format(cmd,
- stderr))
-
- interface = {'name': name,
- 'virtualinterface': '{}'.format(vintname),
- 'bridge': '{}'.format(inum)}
+ "Couldn't execute the command {}, {}.".format(cmd, stderr)
+ )
+
+ interface = {
+ "name": name,
+ "virtualinterface": "{}".format(vintname),
+ "bridge": "{}".format(inum),
+ }
inum += 1
interfaces_with_virtual_interfaces.append(interface)
@@ -1743,49 +1802,58 @@ class AutoConfig(object):
print("\nThis the current bridge configuration:")
VPPUtil.show_bridge(node)
question = "\nWould you like to keep this configuration [Y/n]? "
- answer = self._ask_user_yn(question, 'y')
- if answer == 'y':
+ answer = self._ask_user_yn(question, "y")
+ if answer == "y":
continue
# Create a script that builds a bridge configuration with
# physical interfaces and virtual interfaces
ints_with_vints = self._create_vints_questions(node)
- content = ''
+ content = ""
for intf in ints_with_vints:
- vhoststr = '\n'.join([
- 'comment { The following command creates the socket }',
- 'comment { and returns a virtual interface }',
- 'comment {{ create vhost-user socket '
- '/var/run/vpp/sock{}.sock server }}\n'.format(
- intf['bridge'])
- ])
-
- setintdnstr = 'set interface state {} down\n'.format(
- intf['name'])
-
- setintbrstr = 'set interface l2 bridge {} {}\n'.format(
- intf['name'], intf['bridge'])
- setvintbrstr = 'set interface l2 bridge {} {}\n'.format(
- intf['virtualinterface'], intf['bridge'])
+ vhoststr = "\n".join(
+ [
+ "comment { The following command creates the socket }",
+ "comment { and returns a virtual interface }",
+ "comment {{ create vhost-user socket "
+ "/var/run/vpp/sock{}.sock server }}\n".format(intf["bridge"]),
+ ]
+ )
+
+ setintdnstr = "set interface state {} down\n".format(intf["name"])
+
+ setintbrstr = "set interface l2 bridge {} {}\n".format(
+ intf["name"], intf["bridge"]
+ )
+ setvintbrstr = "set interface l2 bridge {} {}\n".format(
+ intf["virtualinterface"], intf["bridge"]
+ )
# set interface state VirtualEthernet/0/0/0 up
- setintvststr = 'set interface state {} up\n'.format(
- intf['virtualinterface'])
+ setintvststr = "set interface state {} up\n".format(
+ intf["virtualinterface"]
+ )
# set interface state VirtualEthernet/0/0/0 down
- setintupstr = 'set interface state {} up\n'.format(
- intf['name'])
-
- content += vhoststr + setintdnstr + setintbrstr + setvintbrstr + setintvststr + setintupstr
+ setintupstr = "set interface state {} up\n".format(intf["name"])
+
+ content += (
+ vhoststr
+ + setintdnstr
+ + setintbrstr
+ + setvintbrstr
+ + setintvststr
+ + setintupstr
+ )
# Write the content to the script
- rootdir = node['rootdir']
- filename = rootdir + '/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp'
- with open(filename, 'w+') as sfile:
+ rootdir = node["rootdir"]
+ filename = rootdir + "/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp"
+ with open(filename, "w+") as sfile:
sfile.write(content)
# Execute the script
- cmd = 'vppctl exec {}'.format(filename)
+ cmd = "vppctl exec {}".format(filename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
@@ -1813,12 +1881,13 @@ class AutoConfig(object):
# First delete all the Virtual interfaces
for intf in sorted(interfaces.items()):
name = intf[0]
- if name[:7] == 'Virtual':
- cmd = 'vppctl delete vhost-user {}'.format(name)
+ if name[:7] == "Virtual":
+ cmd = "vppctl delete vhost-user {}".format(name)
(ret, stdout, stderr) = vpputl.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd, node['host'], stderr))
+ logging.debug(
+ "{} failed on node {} {}".format(cmd, node["host"], stderr)
+ )
# Create a virtual interface, for each interface the user wants to use
interfaces = vpputl.get_hardware(node)
@@ -1828,39 +1897,45 @@ class AutoConfig(object):
inum = 1
while True:
- print('\nPlease pick one interface to connect to the iperf VM.')
+ print("\nPlease pick one interface to connect to the iperf VM.")
for intf in sorted(interfaces.items()):
name = intf[0]
- if name == 'local0':
+ if name == "local0":
continue
- question = "Would you like connect this interface {} to " \
- "the VM [y/N]? ".format(name)
- answer = self._ask_user_yn(question, 'n')
- if answer == 'y':
- self._sockfilename = '/var/run/vpp/{}.sock'.format(
- name.replace('/', '_'))
+ question = (
+ "Would you like connect this interface {} to "
+ "the VM [y/N]? ".format(name)
+ )
+ answer = self._ask_user_yn(question, "n")
+ if answer == "y":
+ self._sockfilename = "/var/run/vpp/{}.sock".format(
+ name.replace("/", "_")
+ )
if os.path.exists(self._sockfilename):
os.remove(self._sockfilename)
- cmd = 'vppctl create vhost-user socket {} server'.format(
- self._sockfilename)
+ cmd = "vppctl create vhost-user socket {} server".format(
+ self._sockfilename
+ )
(ret, stdout, stderr) = vpputl.exec_command(cmd)
if ret != 0:
raise RuntimeError(
- "Couldn't execute the command {}, {}.".format(
- cmd, stderr))
- vintname = stdout.rstrip('\r\n')
+ "Couldn't execute the command {}, {}.".format(cmd, stderr)
+ )
+ vintname = stdout.rstrip("\r\n")
- cmd = 'chmod 777 {}'.format(self._sockfilename)
+ cmd = "chmod 777 {}".format(self._sockfilename)
(ret, stdout, stderr) = vpputl.exec_command(cmd)
if ret != 0:
raise RuntimeError(
- "Couldn't execute the command {}, {}.".format(
- cmd, stderr))
-
- interface = {'name': name,
- 'virtualinterface': '{}'.format(vintname),
- 'bridge': '{}'.format(inum)}
+ "Couldn't execute the command {}, {}.".format(cmd, stderr)
+ )
+
+ interface = {
+ "name": name,
+ "virtualinterface": "{}".format(vintname),
+ "bridge": "{}".format(inum),
+ }
inum += 1
interfaces_with_virtual_interfaces.append(interface)
return interfaces_with_virtual_interfaces
@@ -1879,52 +1954,62 @@ class AutoConfig(object):
print("\nThis the current bridge configuration:")
ifaces = VPPUtil.show_bridge(node)
question = "\nWould you like to keep this configuration [Y/n]? "
- answer = self._ask_user_yn(question, 'y')
- if answer == 'y':
- self._sockfilename = '/var/run/vpp/{}.sock'.format(
- ifaces[0]['name'].replace('/', '_'))
+ answer = self._ask_user_yn(question, "y")
+ if answer == "y":
+ self._sockfilename = "/var/run/vpp/{}.sock".format(
+ ifaces[0]["name"].replace("/", "_")
+ )
if os.path.exists(self._sockfilename):
continue
# Create a script that builds a bridge configuration with
# physical interfaces and virtual interfaces
ints_with_vints = self._iperf_vm_questions(node)
- content = ''
+ content = ""
for intf in ints_with_vints:
- vhoststr = '\n'.join([
- 'comment { The following command creates the socket }',
- 'comment { and returns a virtual interface }',
- 'comment {{ create vhost-user socket '
- '/var/run/vpp/sock{}.sock server }}\n'.format(
- intf['bridge'])
- ])
-
- setintdnstr = 'set interface state {} down\n'.format(
- intf['name'])
-
- setintbrstr = 'set interface l2 bridge {} {}\n'.format(
- intf['name'], intf['bridge'])
- setvintbrstr = 'set interface l2 bridge {} {}\n'.format(
- intf['virtualinterface'], intf['bridge'])
+ vhoststr = "\n".join(
+ [
+ "comment { The following command creates the socket }",
+ "comment { and returns a virtual interface }",
+ "comment {{ create vhost-user socket "
+ "/var/run/vpp/sock{}.sock server }}\n".format(intf["bridge"]),
+ ]
+ )
+
+ setintdnstr = "set interface state {} down\n".format(intf["name"])
+
+ setintbrstr = "set interface l2 bridge {} {}\n".format(
+ intf["name"], intf["bridge"]
+ )
+ setvintbrstr = "set interface l2 bridge {} {}\n".format(
+ intf["virtualinterface"], intf["bridge"]
+ )
# set interface state VirtualEthernet/0/0/0 up
- setintvststr = 'set interface state {} up\n'.format(
- intf['virtualinterface'])
+ setintvststr = "set interface state {} up\n".format(
+ intf["virtualinterface"]
+ )
# set interface state VirtualEthernet/0/0/0 down
- setintupstr = 'set interface state {} up\n'.format(
- intf['name'])
-
- content += vhoststr + setintdnstr + setintbrstr + setvintbrstr + setintvststr + setintupstr
+ setintupstr = "set interface state {} up\n".format(intf["name"])
+
+ content += (
+ vhoststr
+ + setintdnstr
+ + setintbrstr
+ + setvintbrstr
+ + setintvststr
+ + setintupstr
+ )
# Write the content to the script
- rootdir = node['rootdir']
- filename = rootdir + '/vpp/vpp-config/scripts/create_iperf_vm'
- with open(filename, 'w+') as sfile:
+ rootdir = node["rootdir"]
+ filename = rootdir + "/vpp/vpp-config/scripts/create_iperf_vm"
+ with open(filename, "w+") as sfile:
sfile.write(content)
# Execute the script
- cmd = 'vppctl exec {}'.format(filename)
+ cmd = "vppctl exec {}".format(filename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
@@ -1943,21 +2028,22 @@ class AutoConfig(object):
:type name: str
"""
- cmd = 'virsh list'
+ cmd = "virsh list"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
raise RuntimeError(
- "Couldn't execute the command {} : {}".format(cmd, stderr))
+ "Couldn't execute the command {} : {}".format(cmd, stderr)
+ )
if re.findall(name, stdout):
- cmd = 'virsh destroy {}'.format(name)
+ cmd = "virsh destroy {}".format(name)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
raise RuntimeError(
- "Couldn't execute the command {} : {}".format(
- cmd, stderr))
+ "Couldn't execute the command {} : {}".format(cmd, stderr)
+ )
def create_iperf_vm(self, vmname):
"""
@@ -1968,36 +2054,39 @@ class AutoConfig(object):
# Read the iperf VM template file
distro = VPPUtil.get_linux_distro()
- if distro[0] == 'Ubuntu':
- tfilename = \
- '{}/vpp/vpp-config/configs/iperf-ubuntu.xml.template'.format(
- self._rootdir)
+ if distro[0] == "Ubuntu":
+ tfilename = "{}/vpp/vpp-config/configs/iperf-ubuntu.xml.template".format(
+ self._rootdir
+ )
else:
- tfilename = \
- '{}/vpp/vpp-config/configs/iperf-centos.xml.template'.format(
- self._rootdir)
+ tfilename = "{}/vpp/vpp-config/configs/iperf-centos.xml.template".format(
+ self._rootdir
+ )
- with open(tfilename, 'r') as tfile:
+ with open(tfilename, "r") as tfile:
tcontents = tfile.read()
tfile.close()
# Add the variables
- imagename = '{}/vpp/vpp-config/{}'.format(
- self._rootdir, IPERFVM_IMAGE)
- isoname = '{}/vpp/vpp-config/{}'.format(self._rootdir, IPERFVM_ISO)
- tcontents = tcontents.format(vmname=vmname, imagename=imagename,
- isoname=isoname,
- vhostsocketname=self._sockfilename)
+ imagename = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_IMAGE)
+ isoname = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_ISO)
+ tcontents = tcontents.format(
+ vmname=vmname,
+ imagename=imagename,
+ isoname=isoname,
+ vhostsocketname=self._sockfilename,
+ )
# Write the xml
- ifilename = '{}/vpp/vpp-config/{}'.format(self._rootdir, IPERFVM_XML)
- with open(ifilename, 'w+') as ifile:
+ ifilename = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_XML)
+ with open(ifilename, "w+") as ifile:
ifile.write(tcontents)
ifile.close()
- cmd = 'virsh create {}'.format(ifilename)
+ cmd = "virsh create {}".format(ifilename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
raise RuntimeError(
- "Couldn't execute the command {} : {}".format(cmd, stderr))
+ "Couldn't execute the command {} : {}".format(cmd, stderr)
+ )
diff --git a/extras/vpp_config/vpplib/CpuUtils.py b/extras/vpp_config/vpplib/CpuUtils.py
index 23f418d33be..f6ba3d74746 100644
--- a/extras/vpp_config/vpplib/CpuUtils.py
+++ b/extras/vpp_config/vpplib/CpuUtils.py
@@ -78,13 +78,14 @@ class CpuUtils(object):
# 1,1,0,0,,1,1,1,0
if ret != 0:
raise RuntimeError(
- "Failed to execute ssh command, ret: {} err: {}".format(
- ret, stderr))
- node['cpuinfo'] = list()
+ "Failed to execute ssh command, ret: {} err: {}".format(ret, stderr)
+ )
+ node["cpuinfo"] = list()
for line in stdout.split("\n"):
- if line != '' and line[0] != "#":
- node['cpuinfo'].append([CpuUtils.__str2int(x) for x in
- line.split(",")])
+ if line != "" and line[0] != "#":
+ node["cpuinfo"].append(
+ [CpuUtils.__str2int(x) for x in line.split(",")]
+ )
@staticmethod
def cpu_node_count(node):
@@ -137,13 +138,14 @@ class CpuUtils(object):
if smt_enabled and not smt_used:
cpu_list_len = len(cpu_list)
- cpu_list = cpu_list[:cpu_list_len // CpuUtils.NR_OF_THREADS]
+ cpu_list = cpu_list[: cpu_list_len // CpuUtils.NR_OF_THREADS]
return cpu_list
@staticmethod
- def cpu_slice_of_list_per_node(node, cpu_node, skip_cnt=0, cpu_cnt=0,
- smt_used=False):
+ def cpu_slice_of_list_per_node(
+ node, cpu_node, skip_cnt=0, cpu_cnt=0, smt_used=False
+ ):
"""Return string of node related list of CPU numbers.
:param node: Node dictionary with cpuinfo.
@@ -171,20 +173,20 @@ class CpuUtils(object):
cpu_cnt = cpu_list_len - skip_cnt
if smt_used:
- cpu_list_0 = cpu_list[:cpu_list_len // CpuUtils.NR_OF_THREADS]
- cpu_list_1 = cpu_list[cpu_list_len // CpuUtils.NR_OF_THREADS:]
- cpu_list = [cpu for cpu in cpu_list_0[skip_cnt:skip_cnt + cpu_cnt]]
- cpu_list_ex = [cpu for cpu in
- cpu_list_1[skip_cnt:skip_cnt + cpu_cnt]]
+ cpu_list_0 = cpu_list[: cpu_list_len // CpuUtils.NR_OF_THREADS]
+ cpu_list_1 = cpu_list[cpu_list_len // CpuUtils.NR_OF_THREADS :]
+ cpu_list = [cpu for cpu in cpu_list_0[skip_cnt : skip_cnt + cpu_cnt]]
+ cpu_list_ex = [cpu for cpu in cpu_list_1[skip_cnt : skip_cnt + cpu_cnt]]
cpu_list.extend(cpu_list_ex)
else:
- cpu_list = [cpu for cpu in cpu_list[skip_cnt:skip_cnt + cpu_cnt]]
+ cpu_list = [cpu for cpu in cpu_list[skip_cnt : skip_cnt + cpu_cnt]]
return cpu_list
@staticmethod
- def cpu_list_per_node_str(node, cpu_node, skip_cnt=0, cpu_cnt=0, sep=",",
- smt_used=False):
+ def cpu_list_per_node_str(
+ node, cpu_node, skip_cnt=0, cpu_cnt=0, sep=",", smt_used=False
+ ):
"""Return string of node related list of CPU numbers.
:param node: Node dictionary with cpuinfo.
@@ -203,15 +205,15 @@ class CpuUtils(object):
:rtype: str
"""
- cpu_list = CpuUtils.cpu_slice_of_list_per_node(node, cpu_node,
- skip_cnt=skip_cnt,
- cpu_cnt=cpu_cnt,
- smt_used=smt_used)
+ cpu_list = CpuUtils.cpu_slice_of_list_per_node(
+ node, cpu_node, skip_cnt=skip_cnt, cpu_cnt=cpu_cnt, smt_used=smt_used
+ )
return sep.join(str(cpu) for cpu in cpu_list)
@staticmethod
- def cpu_range_per_node_str(node, cpu_node, skip_cnt=0, cpu_cnt=0, sep="-",
- smt_used=False):
+ def cpu_range_per_node_str(
+ node, cpu_node, skip_cnt=0, cpu_cnt=0, sep="-", smt_used=False
+ ):
"""Return string of node related range of CPU numbers, e.g. 0-4.
:param node: Node dictionary with cpuinfo.
@@ -230,18 +232,16 @@ class CpuUtils(object):
:rtype: str
"""
- cpu_list = CpuUtils.cpu_slice_of_list_per_node(node, cpu_node,
- skip_cnt=skip_cnt,
- cpu_cnt=cpu_cnt,
- smt_used=smt_used)
+ cpu_list = CpuUtils.cpu_slice_of_list_per_node(
+ node, cpu_node, skip_cnt=skip_cnt, cpu_cnt=cpu_cnt, smt_used=smt_used
+ )
if smt_used:
cpu_list_len = len(cpu_list)
- cpu_list_0 = cpu_list[:cpu_list_len // CpuUtils.NR_OF_THREADS]
- cpu_list_1 = cpu_list[cpu_list_len // CpuUtils.NR_OF_THREADS:]
- cpu_range = "{}{}{},{}{}{}".format(cpu_list_0[0], sep,
- cpu_list_0[-1],
- cpu_list_1[0], sep,
- cpu_list_1[-1])
+ cpu_list_0 = cpu_list[: cpu_list_len // CpuUtils.NR_OF_THREADS]
+ cpu_list_1 = cpu_list[cpu_list_len // CpuUtils.NR_OF_THREADS :]
+ cpu_range = "{}{}{},{}{}{}".format(
+ cpu_list_0[0], sep, cpu_list_0[-1], cpu_list_1[0], sep, cpu_list_1[-1]
+ )
else:
cpu_range = "{}{}{}".format(cpu_list[0], sep, cpu_list[-1])
@@ -260,28 +260,30 @@ class CpuUtils(object):
cmd = "lscpu"
ret, stdout, stderr = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError("lscpu command failed on node {} {}."
- .format(node['host'], stderr))
+ raise RuntimeError(
+ "lscpu command failed on node {} {}.".format(node["host"], stderr)
+ )
cpuinfo = {}
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
for line in lines:
- if line != '':
- linesplit = re.split(r':\s+', line)
+ if line != "":
+ linesplit = re.split(r":\s+", line)
cpuinfo[linesplit[0]] = linesplit[1]
cmd = "cat /proc/*/task/*/stat | awk '{print $1" "$2" "$39}'"
ret, stdout, stderr = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError("cat command failed on node {} {}."
- .format(node['host'], stderr))
+ raise RuntimeError(
+ "cat command failed on node {} {}.".format(node["host"], stderr)
+ )
vpp_processes = {}
- vpp_lines = re.findall(r'\w+\(vpp_\w+\)\w+', stdout)
+ vpp_lines = re.findall(r"\w+\(vpp_\w+\)\w+", stdout)
for line in vpp_lines:
- linesplit = re.split(r'\w+\(', line)[1].split(')')
+ linesplit = re.split(r"\w+\(", line)[1].split(")")
vpp_processes[linesplit[0]] = linesplit[1]
- cpuinfo['vpp_processes'] = vpp_processes
+ cpuinfo["vpp_processes"] = vpp_processes
return cpuinfo
diff --git a/extras/vpp_config/vpplib/QemuUtils.py b/extras/vpp_config/vpplib/QemuUtils.py
index 0b7e08b12d8..e1da7ae72bf 100644
--- a/extras/vpp_config/vpplib/QemuUtils.py
+++ b/extras/vpp_config/vpplib/QemuUtils.py
@@ -12,7 +12,7 @@
# limitations under the License.
"""QEMU utilities library."""
-from __future__ import absolute_import, division
+from __future__ import absolute_import, division
from time import time, sleep
import json
@@ -24,12 +24,13 @@ from vpplib.constants import Constants
class NodeType(object):
"""Defines node types used in topology dictionaries."""
+
# Device Under Test (this node has VPP running on it)
- DUT = 'DUT'
+ DUT = "DUT"
# Traffic Generator (this node has traffic generator on it)
- TG = 'TG'
+ TG = "TG"
# Virtual Machine (this node running on DUT node)
- VM = 'VM'
+ VM = "VM"
class QemuUtils(object):
@@ -39,43 +40,46 @@ class QemuUtils(object):
def __init__(self, qemu_id=1):
self._qemu_id = qemu_id
# Path to QEMU binary
- self._qemu_bin = '/usr/bin/qemu-system-x86_64'
+ self._qemu_bin = "/usr/bin/qemu-system-x86_64"
# QEMU Machine Protocol socket
- self._qmp_sock = '/tmp/qmp{0}.sock'.format(self._qemu_id)
+ self._qmp_sock = "/tmp/qmp{0}.sock".format(self._qemu_id)
# QEMU Guest Agent socket
- self._qga_sock = '/tmp/qga{0}.sock'.format(self._qemu_id)
+ self._qga_sock = "/tmp/qga{0}.sock".format(self._qemu_id)
# QEMU PID file
- self._pid_file = '/tmp/qemu{0}.pid'.format(self._qemu_id)
+ self._pid_file = "/tmp/qemu{0}.pid".format(self._qemu_id)
self._qemu_opt = {}
# Default 1 CPU.
- self._qemu_opt['smp'] = '-smp 1,sockets=1,cores=1,threads=1'
+ self._qemu_opt["smp"] = "-smp 1,sockets=1,cores=1,threads=1"
# Daemonize the QEMU process after initialization. Default one
# management interface.
- self._qemu_opt['options'] = '-cpu host -daemonize -enable-kvm ' \
- '-machine pc,accel=kvm,usb=off,mem-merge=off ' \
- '-net nic,macaddr=52:54:00:00:{0:02x}:ff -balloon none'\
- .format(self._qemu_id)
- self._qemu_opt['ssh_fwd_port'] = 10021 + qemu_id
+ self._qemu_opt["options"] = (
+ "-cpu host -daemonize -enable-kvm "
+ "-machine pc,accel=kvm,usb=off,mem-merge=off "
+ "-net nic,macaddr=52:54:00:00:{0:02x}:ff -balloon none".format(
+ self._qemu_id
+ )
+ )
+ self._qemu_opt["ssh_fwd_port"] = 10021 + qemu_id
# Default serial console port
- self._qemu_opt['serial_port'] = 4555 + qemu_id
+ self._qemu_opt["serial_port"] = 4555 + qemu_id
# Default 512MB virtual RAM
- self._qemu_opt['mem_size'] = 512
+ self._qemu_opt["mem_size"] = 512
# Default huge page mount point, required for Vhost-user interfaces.
- self._qemu_opt['huge_mnt'] = '/mnt/huge'
+ self._qemu_opt["huge_mnt"] = "/mnt/huge"
# Default do not allocate huge pages.
- self._qemu_opt['huge_allocate'] = False
+ self._qemu_opt["huge_allocate"] = False
# Default image for CSIT virl setup
- self._qemu_opt['disk_image'] = '/var/lib/vm/vhost-nested.img'
+ self._qemu_opt["disk_image"] = "/var/lib/vm/vhost-nested.img"
# VM node info dict
self._vm_info = {
- 'type': NodeType.VM,
- 'port': self._qemu_opt['ssh_fwd_port'],
- 'username': 'cisco',
- 'password': 'cisco',
- 'interfaces': {},
+ "type": NodeType.VM,
+ "port": self._qemu_opt["ssh_fwd_port"],
+ "username": "cisco",
+ "password": "cisco",
+ "interfaces": {},
}
# Virtio queue count
- self._qemu_opt['queues'] = 1
+ self._qemu_opt["queues"] = 1
self._vhost_id = 0
self._ssh = None
self._node = None
@@ -101,9 +105,9 @@ class QemuUtils(object):
:type threads: int
:type sockets: int
"""
- self._qemu_opt['smp'] = \
- '-smp {},cores={},threads={},sockets={}'.format(
- cpus, cores, threads, sockets)
+ self._qemu_opt["smp"] = "-smp {},cores={},threads={},sockets={}".format(
+ cpus, cores, threads, sockets
+ )
def qemu_set_ssh_fwd_port(self, fwd_port):
"""Set host port for guest SSH forwarding.
@@ -111,8 +115,8 @@ class QemuUtils(object):
:param fwd_port: Port number on host for guest SSH forwarding.
:type fwd_port: int
"""
- self._qemu_opt['ssh_fwd_port'] = fwd_port
- self._vm_info['port'] = fwd_port
+ self._qemu_opt["ssh_fwd_port"] = fwd_port
+ self._vm_info["port"] = fwd_port
def qemu_set_serial_port(self, port):
"""Set serial console port.
@@ -120,7 +124,7 @@ class QemuUtils(object):
:param port: Serial console port.
:type port: int
"""
- self._qemu_opt['serial_port'] = port
+ self._qemu_opt["serial_port"] = port
def qemu_set_mem_size(self, mem_size):
"""Set virtual RAM size.
@@ -128,7 +132,7 @@ class QemuUtils(object):
:param mem_size: RAM size in Mega Bytes.
:type mem_size: int
"""
- self._qemu_opt['mem_size'] = int(mem_size)
+ self._qemu_opt["mem_size"] = int(mem_size)
def qemu_set_huge_mnt(self, huge_mnt):
"""Set hugefile mount point.
@@ -136,11 +140,11 @@ class QemuUtils(object):
:param huge_mnt: System hugefile mount point.
:type huge_mnt: int
"""
- self._qemu_opt['huge_mnt'] = huge_mnt
+ self._qemu_opt["huge_mnt"] = huge_mnt
def qemu_set_huge_allocate(self):
"""Set flag to allocate more huge pages if needed."""
- self._qemu_opt['huge_allocate'] = True
+ self._qemu_opt["huge_allocate"] = True
def qemu_set_disk_image(self, disk_image):
"""Set disk image.
@@ -148,7 +152,7 @@ class QemuUtils(object):
:param disk_image: Path of the disk image.
:type disk_image: str
"""
- self._qemu_opt['disk_image'] = disk_image
+ self._qemu_opt["disk_image"] = disk_image
def qemu_set_affinity(self, *host_cpus):
"""Set qemu affinity by getting thread PIDs via QMP and taskset to list
@@ -157,36 +161,41 @@ class QemuUtils(object):
:param host_cpus: List of CPU cores.
:type host_cpus: list
"""
- qemu_cpus = self._qemu_qmp_exec('query-cpus')['return']
+ qemu_cpus = self._qemu_qmp_exec("query-cpus")["return"]
if len(qemu_cpus) != len(host_cpus):
- logging.debug('Host CPU count {0}, Qemu Thread count {1}'.format(
- len(host_cpus), len(qemu_cpus)))
- raise ValueError('Host CPU count must match Qemu Thread count')
+ logging.debug(
+ "Host CPU count {0}, Qemu Thread count {1}".format(
+ len(host_cpus), len(qemu_cpus)
+ )
+ )
+ raise ValueError("Host CPU count must match Qemu Thread count")
for qemu_cpu, host_cpu in zip(qemu_cpus, host_cpus):
- cmd = 'taskset -pc {0} {1}'.format(host_cpu, qemu_cpu['thread_id'])
+ cmd = "taskset -pc {0} {1}".format(host_cpu, qemu_cpu["thread_id"])
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd)
if int(ret_code) != 0:
- logging.debug('Set affinity failed {0}'.format(stderr))
- raise RuntimeError('Set affinity failed on {0}'.format(
- self._node['host']))
+ logging.debug("Set affinity failed {0}".format(stderr))
+ raise RuntimeError(
+ "Set affinity failed on {0}".format(self._node["host"])
+ )
def qemu_set_scheduler_policy(self):
"""Set scheduler policy to SCHED_RR with priority 1 for all Qemu CPU
- processes.
+ processes.
- :raises RuntimeError: Set scheduler policy failed.
+ :raises RuntimeError: Set scheduler policy failed.
"""
- qemu_cpus = self._qemu_qmp_exec('query-cpus')['return']
+ qemu_cpus = self._qemu_qmp_exec("query-cpus")["return"]
for qemu_cpu in qemu_cpus:
- cmd = 'chrt -r -p 1 {0}'.format(qemu_cpu['thread_id'])
+ cmd = "chrt -r -p 1 {0}".format(qemu_cpu["thread_id"])
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd)
if int(ret_code) != 0:
- logging.debug('Set SCHED_RR failed {0}'.format(stderr))
- raise RuntimeError('Set SCHED_RR failed on {0}'.format(
- self._node['host']))
+ logging.debug("Set SCHED_RR failed {0}".format(stderr))
+ raise RuntimeError(
+ "Set SCHED_RR failed on {0}".format(self._node["host"])
+ )
def qemu_set_node(self, node):
"""Set node to run QEMU on.
@@ -195,7 +204,7 @@ class QemuUtils(object):
:type node: dict
"""
self._node = node
- self._vm_info['host'] = node['host']
+ self._vm_info["host"] = node["host"]
def qemu_add_vhost_user_if(self, socket, server=True, mac=None):
"""Add Vhost-user interface.
@@ -210,31 +219,33 @@ class QemuUtils(object):
"""
self._vhost_id += 1
# Create unix socket character device.
- chardev = ' -chardev socket,id=char{0},path={1}'.format(self._vhost_id,
- socket)
+ chardev = " -chardev socket,id=char{0},path={1}".format(self._vhost_id, socket)
if server is True:
- chardev += ',server'
- self._qemu_opt['options'] += chardev
+ chardev += ",server"
+ self._qemu_opt["options"] += chardev
# Create Vhost-user network backend.
- netdev = (' -netdev vhost-user,id=vhost{0},chardev=char{0},queues={1}'
- .format(self._vhost_id, self._qemu_opt['queues']))
- self._qemu_opt['options'] += netdev
+ netdev = " -netdev vhost-user,id=vhost{0},chardev=char{0},queues={1}".format(
+ self._vhost_id, self._qemu_opt["queues"]
+ )
+ self._qemu_opt["options"] += netdev
# If MAC is not specified use auto-generated MAC address based on
# template 52:54:00:00:<qemu_id>:<vhost_id>, e.g. vhost1 MAC of QEMU
# with ID 1 is 52:54:00:00:01:01
if mac is None:
- mac = '52:54:00:00:{0:02x}:{1:02x}'.\
- format(self._qemu_id, self._vhost_id)
- extend_options = 'mq=on,csum=off,gso=off,guest_tso4=off,'\
- 'guest_tso6=off,guest_ecn=off,mrg_rxbuf=off'
+ mac = "52:54:00:00:{0:02x}:{1:02x}".format(self._qemu_id, self._vhost_id)
+ extend_options = (
+ "mq=on,csum=off,gso=off,guest_tso4=off,"
+ "guest_tso6=off,guest_ecn=off,mrg_rxbuf=off"
+ )
# Create Virtio network device.
- device = ' -device virtio-net-pci,netdev=vhost{0},mac={1},{2}'.format(
- self._vhost_id, mac, extend_options)
- self._qemu_opt['options'] += device
+ device = " -device virtio-net-pci,netdev=vhost{0},mac={1},{2}".format(
+ self._vhost_id, mac, extend_options
+ )
+ self._qemu_opt["options"] += device
# Add interface MAC and socket to the node dict
- if_data = {'mac_address': mac, 'socket': socket}
- if_name = 'vhost{}'.format(self._vhost_id)
- self._vm_info['interfaces'][if_name] = if_data
+ if_data = {"mac_address": mac, "socket": socket}
+ if_name = "vhost{}".format(self._vhost_id)
+ self._vm_info["interfaces"][if_name] = if_data
# Add socket to the socket list
self._socks.append(socket)
@@ -250,41 +261,44 @@ class QemuUtils(object):
response will contain the "error" keyword instead of "return".
"""
# To enter command mode, the qmp_capabilities command must be issued.
- qmp_cmd = 'echo "{ \\"execute\\": \\"qmp_capabilities\\" }' \
- '{ \\"execute\\": \\"' + cmd + \
- '\\" }" | sudo -S socat - UNIX-CONNECT:' + self._qmp_sock
+ qmp_cmd = (
+ 'echo "{ \\"execute\\": \\"qmp_capabilities\\" }'
+ '{ \\"execute\\": \\"'
+ + cmd
+ + '\\" }" | sudo -S socat - UNIX-CONNECT:'
+ + self._qmp_sock
+ )
(ret_code, stdout, stderr) = self._ssh.exec_command(qmp_cmd)
if int(ret_code) != 0:
- logging.debug('QMP execute failed {0}'.format(stderr))
- raise RuntimeError('QMP execute "{0}"'
- ' failed on {1}'.format(
- cmd, self._node['host']))
+ logging.debug("QMP execute failed {0}".format(stderr))
+ raise RuntimeError(
+ 'QMP execute "{0}"' " failed on {1}".format(cmd, self._node["host"])
+ )
logging.debug(stdout)
# Skip capabilities negotiation messages.
out_list = stdout.splitlines()
if len(out_list) < 3:
- raise RuntimeError('Invalid QMP output on {0}'.format(
- self._node['host']))
+ raise RuntimeError("Invalid QMP output on {0}".format(self._node["host"]))
return json.loads(out_list[2])
def _qemu_qga_flush(self):
- """Flush the QGA parser state
- """
- qga_cmd = '(printf "\xFF"; sleep 1) | ' \
- 'sudo -S socat - UNIX-CONNECT:' + \
- self._qga_sock
+ """Flush the QGA parser state"""
+ qga_cmd = (
+ '(printf "\xFF"; sleep 1) | '
+ "sudo -S socat - UNIX-CONNECT:" + self._qga_sock
+ )
# TODO: probably need something else
(ret_code, stdout, stderr) = self._ssh.exec_command(qga_cmd)
if int(ret_code) != 0:
- logging.debug('QGA execute failed {0}'.format(stderr))
- raise RuntimeError('QGA execute "{0}" '
- 'failed on {1}'.format(qga_cmd,
- self._node['host']))
+ logging.debug("QGA execute failed {0}".format(stderr))
+ raise RuntimeError(
+ 'QGA execute "{0}" ' "failed on {1}".format(qga_cmd, self._node["host"])
+ )
logging.debug(stdout)
if not stdout:
return {}
- return json.loads(stdout.split('\n', 1)[0])
+ return json.loads(stdout.split("\n", 1)[0])
def _qemu_qga_exec(self, cmd):
"""Execute QGA command.
@@ -294,20 +308,22 @@ class QemuUtils(object):
:param cmd: QGA command to execute.
:type cmd: str
"""
- qga_cmd = '(echo "{ \\"execute\\": \\"' + \
- cmd + \
- '\\" }"; sleep 1) | sudo -S socat - UNIX-CONNECT:' + \
- self._qga_sock
+ qga_cmd = (
+ '(echo "{ \\"execute\\": \\"'
+ + cmd
+ + '\\" }"; sleep 1) | sudo -S socat - UNIX-CONNECT:'
+ + self._qga_sock
+ )
(ret_code, stdout, stderr) = self._ssh.exec_command(qga_cmd)
if int(ret_code) != 0:
- logging.debug('QGA execute failed {0}'.format(stderr))
- raise RuntimeError('QGA execute "{0}"'
- ' failed on {1}'.format(
- cmd, self._node['host']))
+ logging.debug("QGA execute failed {0}".format(stderr))
+ raise RuntimeError(
+ 'QGA execute "{0}"' " failed on {1}".format(cmd, self._node["host"])
+ )
logging.debug(stdout)
if not stdout:
return {}
- return json.loads(stdout.split('\n', 1)[0])
+ return json.loads(stdout.split("\n", 1)[0])
def _wait_until_vm_boot(self, timeout=60):
"""Wait until QEMU VM is booted.
@@ -320,65 +336,69 @@ class QemuUtils(object):
start = time()
while True:
if time() - start > timeout:
- raise RuntimeError('timeout, VM {0} not booted on {1}'.format(
- self._qemu_opt['disk_image'], self._node['host']))
+ raise RuntimeError(
+ "timeout, VM {0} not booted on {1}".format(
+ self._qemu_opt["disk_image"], self._node["host"]
+ )
+ )
out = None
try:
self._qemu_qga_flush()
- out = self._qemu_qga_exec('guest-ping')
+ out = self._qemu_qga_exec("guest-ping")
except ValueError:
- logging.debug(
- 'QGA guest-ping unexpected output {}'.format(out))
+ logging.debug("QGA guest-ping unexpected output {}".format(out))
# Empty output - VM not booted yet
if not out:
sleep(5)
# Non-error return - VM booted
- elif out.get('return') is not None:
+ elif out.get("return") is not None:
break
# Skip error and wait
- elif out.get('error') is not None:
+ elif out.get("error") is not None:
sleep(5)
else:
# If there is an unexpected output from QGA guest-info, try
# again until timeout.
- logging.debug(
- 'QGA guest-ping unexpected output {}'.format(out))
+ logging.debug("QGA guest-ping unexpected output {}".format(out))
logging.debug(
- 'VM {0} booted on {1}'.format(self._qemu_opt['disk_image'],
- self._node['host']))
+ "VM {0} booted on {1}".format(
+ self._qemu_opt["disk_image"], self._node["host"]
+ )
+ )
def _update_vm_interfaces(self):
"""Update interface names in VM node dict."""
# Send guest-network-get-interfaces command via QGA, output example:
# {"return": [{"name": "eth0", "hardware-address": "52:54:00:00:04:01"},
# {"name": "eth1", "hardware-address": "52:54:00:00:04:02"}]}
- out = self._qemu_qga_exec('guest-network-get-interfaces')
- interfaces = out.get('return')
+ out = self._qemu_qga_exec("guest-network-get-interfaces")
+ interfaces = out.get("return")
mac_name = {}
if not interfaces:
raise RuntimeError(
- 'Get VM {0} interface list failed on {1}'.format(
- self._qemu_opt['disk_image'], self._node['host']))
+ "Get VM {0} interface list failed on {1}".format(
+ self._qemu_opt["disk_image"], self._node["host"]
+ )
+ )
# Create MAC-name dict
for interface in interfaces:
- if 'hardware-address' not in interface:
+ if "hardware-address" not in interface:
continue
- mac_name[interface['hardware-address']] = interface['name']
+ mac_name[interface["hardware-address"]] = interface["name"]
# Match interface by MAC and save interface name
- for interface in self._vm_info['interfaces'].values():
- mac = interface.get('mac_address')
+ for interface in self._vm_info["interfaces"].values():
+ mac = interface.get("mac_address")
if_name = mac_name.get(mac)
if if_name is None:
- logging.debug(
- 'Interface name for MAC {} not found'.format(mac))
+ logging.debug("Interface name for MAC {} not found".format(mac))
else:
- interface['name'] = if_name
+ interface["name"] = if_name
def _huge_page_check(self, allocate=False):
"""Huge page check."""
- huge_mnt = self._qemu_opt.get('huge_mnt')
- mem_size = self._qemu_opt.get('mem_size')
+ huge_mnt = self._qemu_opt.get("huge_mnt")
+ mem_size = self._qemu_opt.get("mem_size")
# Get huge pages information
huge_size = self._get_huge_page_size()
@@ -391,55 +411,55 @@ class QemuUtils(object):
if allocate:
mem_needed = abs((huge_free * huge_size) - (mem_size * 1024))
huge_to_allocate = ((mem_needed // huge_size) * 2) + huge_total
- max_map_count = huge_to_allocate*4
+ max_map_count = huge_to_allocate * 4
# Increase maximum number of memory map areas a
# process may have
- cmd = \
- 'echo "{0}" | sudo tee /proc/sys/vm/max_map_count'.format(
- max_map_count)
+ cmd = 'echo "{0}" | sudo tee /proc/sys/vm/max_map_count'.format(
+ max_map_count
+ )
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd)
# Increase hugepage count
- cmd = \
- 'echo "{0}" | sudo tee /proc/sys/vm/nr_hugepages'.format(
- huge_to_allocate)
+ cmd = 'echo "{0}" | sudo tee /proc/sys/vm/nr_hugepages'.format(
+ huge_to_allocate
+ )
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd)
if int(ret_code) != 0:
- logging.debug(
- 'Mount huge pages failed {0}'.format(stderr))
+ logging.debug("Mount huge pages failed {0}".format(stderr))
raise RuntimeError(
- 'Mount huge pages failed on {0}'.format(
- self._node['host']))
+ "Mount huge pages failed on {0}".format(self._node["host"])
+ )
# If we do not want to allocate dynamicaly end with error
else:
raise RuntimeError(
- 'Not enough free huge pages: {0}, '
- '{1} MB'.format(huge_free, huge_free * huge_size)
+ "Not enough free huge pages: {0}, "
+ "{1} MB".format(huge_free, huge_free * huge_size)
)
# Check if huge pages mount point exist
has_huge_mnt = False
- (_, output, _) = self._ssh.exec_command('cat /proc/mounts')
+ (_, output, _) = self._ssh.exec_command("cat /proc/mounts")
for line in output.splitlines():
# Try to find something like:
# none /mnt/huge hugetlbfs rw,relatime,pagesize=2048k 0 0
mount = line.split()
- if mount[2] == 'hugetlbfs' and mount[1] == huge_mnt:
+ if mount[2] == "hugetlbfs" and mount[1] == huge_mnt:
has_huge_mnt = True
break
# If huge page mount point not exist create one
if not has_huge_mnt:
- cmd = 'mkdir -p {0}'.format(huge_mnt)
+ cmd = "mkdir -p {0}".format(huge_mnt)
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd)
if int(ret_code) != 0:
- logging.debug('Create mount dir failed: {0}'.format(stderr))
- raise RuntimeError('Create mount dir failed on {0}'.format(
- self._node['host']))
- cmd = 'mount -t hugetlbfs -o pagesize=2048k none {0}'.format(
- huge_mnt)
+ logging.debug("Create mount dir failed: {0}".format(stderr))
+ raise RuntimeError(
+ "Create mount dir failed on {0}".format(self._node["host"])
+ )
+ cmd = "mount -t hugetlbfs -o pagesize=2048k none {0}".format(huge_mnt)
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd)
if int(ret_code) != 0:
- logging.debug('Mount huge pages failed {0}'.format(stderr))
- raise RuntimeError('Mount huge pages failed on {0}'.format(
- self._node['host']))
+ logging.debug("Mount huge pages failed {0}".format(stderr))
+ raise RuntimeError(
+ "Mount huge pages failed on {0}".format(self._node["host"])
+ )
def _get_huge_page_size(self):
"""Get default size of huge pages in system.
@@ -456,11 +476,11 @@ class QemuUtils(object):
try:
huge_size = int(out)
except ValueError:
- logging.debug('Reading huge page size information failed')
+ logging.debug("Reading huge page size information failed")
else:
break
else:
- raise RuntimeError('Getting huge page size information failed.')
+ raise RuntimeError("Getting huge page size information failed.")
return huge_size
def _get_huge_page_free(self, huge_size):
@@ -474,20 +494,21 @@ class QemuUtils(object):
"""
# TODO: add numa aware option
# TODO: remove to dedicated library
- cmd_huge_free = 'cat /sys/kernel/mm/hugepages/hugepages-{0}kB/'\
- 'free_hugepages'.format(huge_size)
+ cmd_huge_free = (
+ "cat /sys/kernel/mm/hugepages/hugepages-{0}kB/"
+ "free_hugepages".format(huge_size)
+ )
for _ in range(3):
(ret, out, _) = self._ssh.exec_command_sudo(cmd_huge_free)
if ret == 0:
try:
huge_free = int(out)
except ValueError:
- logging.debug(
- 'Reading free huge pages information failed')
+ logging.debug("Reading free huge pages information failed")
else:
break
else:
- raise RuntimeError('Getting free huge pages information failed.')
+ raise RuntimeError("Getting free huge pages information failed.")
return huge_free
def _get_huge_page_total(self, huge_size):
@@ -501,20 +522,21 @@ class QemuUtils(object):
"""
# TODO: add numa aware option
# TODO: remove to dedicated library
- cmd_huge_total = 'cat /sys/kernel/mm/hugepages/hugepages-{0}kB/'\
- 'nr_hugepages'.format(huge_size)
+ cmd_huge_total = (
+ "cat /sys/kernel/mm/hugepages/hugepages-{0}kB/"
+ "nr_hugepages".format(huge_size)
+ )
for _ in range(3):
(ret, out, _) = self._ssh.exec_command_sudo(cmd_huge_total)
if ret == 0:
try:
huge_total = int(out)
except ValueError:
- logging.debug(
- 'Reading total huge pages information failed')
+ logging.debug("Reading total huge pages information failed")
else:
break
else:
- raise RuntimeError('Getting total huge pages information failed.')
+ raise RuntimeError("Getting total huge pages information failed.")
return huge_total
def qemu_start(self):
@@ -526,45 +548,63 @@ class QemuUtils(object):
.. warning:: Starts only one VM on the node.
"""
# SSH forwarding
- ssh_fwd = '-net user,hostfwd=tcp::{0}-:22'.format(
- self._qemu_opt.get('ssh_fwd_port'))
+ ssh_fwd = "-net user,hostfwd=tcp::{0}-:22".format(
+ self._qemu_opt.get("ssh_fwd_port")
+ )
# Memory and huge pages
- mem = '-object memory-backend-file,id=mem,size={0}M,mem-path={1},' \
- 'share=on -m {0} -numa node,memdev=mem'.format(
- self._qemu_opt.get('mem_size'), self._qemu_opt.get('huge_mnt'))
+ mem = (
+ "-object memory-backend-file,id=mem,size={0}M,mem-path={1},"
+ "share=on -m {0} -numa node,memdev=mem".format(
+ self._qemu_opt.get("mem_size"), self._qemu_opt.get("huge_mnt")
+ )
+ )
# By default check only if hugepages are available.
# If 'huge_allocate' is set to true try to allocate as well.
- self._huge_page_check(allocate=self._qemu_opt.get('huge_allocate'))
+ self._huge_page_check(allocate=self._qemu_opt.get("huge_allocate"))
# Disk option
- drive = '-drive file={0},format=raw,cache=none,if=virtio'.format(
- self._qemu_opt.get('disk_image'))
+ drive = "-drive file={0},format=raw,cache=none,if=virtio".format(
+ self._qemu_opt.get("disk_image")
+ )
# Setup QMP via unix socket
- qmp = '-qmp unix:{0},server,nowait'.format(self._qmp_sock)
+ qmp = "-qmp unix:{0},server,nowait".format(self._qmp_sock)
# Setup serial console
- serial = '-chardev socket,host=127.0.0.1,port={0},id=gnc0,server,' \
- 'nowait -device isa-serial,chardev=gnc0'.format(
- self._qemu_opt.get('serial_port'))
+ serial = (
+ "-chardev socket,host=127.0.0.1,port={0},id=gnc0,server,"
+ "nowait -device isa-serial,chardev=gnc0".format(
+ self._qemu_opt.get("serial_port")
+ )
+ )
# Setup QGA via chardev (unix socket) and isa-serial channel
- qga = '-chardev socket,path={0},server,nowait,id=qga0 ' \
- '-device isa-serial,chardev=qga0'.format(self._qga_sock)
+ qga = (
+ "-chardev socket,path={0},server,nowait,id=qga0 "
+ "-device isa-serial,chardev=qga0".format(self._qga_sock)
+ )
# Graphic setup
- graphic = '-monitor none -display none -vga none'
+ graphic = "-monitor none -display none -vga none"
# PID file
- pid = '-pidfile {}'.format(self._pid_file)
+ pid = "-pidfile {}".format(self._pid_file)
# Run QEMU
- cmd = '{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}'.format(
- self._qemu_bin, self._qemu_opt.get('smp'), mem, ssh_fwd,
- self._qemu_opt.get('options'),
- drive, qmp, serial, qga, graphic, pid)
+ cmd = "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}".format(
+ self._qemu_bin,
+ self._qemu_opt.get("smp"),
+ mem,
+ ssh_fwd,
+ self._qemu_opt.get("options"),
+ drive,
+ qmp,
+ serial,
+ qga,
+ graphic,
+ pid,
+ )
(ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd, timeout=300)
if int(ret_code) != 0:
- logging.debug('QEMU start failed {0}'.format(stderr))
- raise RuntimeError('QEMU start failed on {0}'.format(
- self._node['host']))
- logging.debug('QEMU running')
+ logging.debug("QEMU start failed {0}".format(stderr))
+ raise RuntimeError("QEMU start failed on {0}".format(self._node["host"]))
+ logging.debug("QEMU running")
# Wait until VM boot
try:
self._wait_until_vm_boot()
@@ -579,40 +619,43 @@ class QemuUtils(object):
def qemu_quit(self):
"""Quit the QEMU emulator."""
- out = self._qemu_qmp_exec('quit')
- err = out.get('error')
+ out = self._qemu_qmp_exec("quit")
+ err = out.get("error")
if err is not None:
- raise RuntimeError('QEMU quit failed on {0}, error: {1}'.format(
- self._node['host'], json.dumps(err)))
+ raise RuntimeError(
+ "QEMU quit failed on {0}, error: {1}".format(
+ self._node["host"], json.dumps(err)
+ )
+ )
def qemu_system_powerdown(self):
"""Power down the system (if supported)."""
- out = self._qemu_qmp_exec('system_powerdown')
- err = out.get('error')
+ out = self._qemu_qmp_exec("system_powerdown")
+ err = out.get("error")
if err is not None:
raise RuntimeError(
- 'QEMU system powerdown failed on {0}, '
- 'error: {1}'.format(self._node['host'], json.dumps(err))
+ "QEMU system powerdown failed on {0}, "
+ "error: {1}".format(self._node["host"], json.dumps(err))
)
def qemu_system_reset(self):
"""Reset the system."""
- out = self._qemu_qmp_exec('system_reset')
- err = out.get('error')
+ out = self._qemu_qmp_exec("system_reset")
+ err = out.get("error")
if err is not None:
raise RuntimeError(
- 'QEMU system reset failed on {0}, '
- 'error: {1}'.format(self._node['host'], json.dumps(err)))
+ "QEMU system reset failed on {0}, "
+ "error: {1}".format(self._node["host"], json.dumps(err))
+ )
def qemu_kill(self):
"""Kill qemu process."""
# Note: in QEMU start phase there are 3 QEMU processes because we
# daemonize QEMU
- self._ssh.exec_command_sudo('chmod +r {}'.format(self._pid_file))
- self._ssh.exec_command_sudo('kill -SIGKILL $(cat {})'
- .format(self._pid_file))
+ self._ssh.exec_command_sudo("chmod +r {}".format(self._pid_file))
+ self._ssh.exec_command_sudo("kill -SIGKILL $(cat {})".format(self._pid_file))
# Delete PID file
- cmd = 'rm -f {}'.format(self._pid_file)
+ cmd = "rm -f {}".format(self._pid_file)
self._ssh.exec_command_sudo(cmd)
def qemu_kill_all(self, node=None):
@@ -623,16 +666,16 @@ class QemuUtils(object):
"""
if node:
self.qemu_set_node(node)
- self._ssh.exec_command_sudo('pkill -SIGKILL qemu')
+ self._ssh.exec_command_sudo("pkill -SIGKILL qemu")
def qemu_clear_socks(self):
"""Remove all sockets created by QEMU."""
# If serial console port still open kill process
- cmd = 'fuser -k {}/tcp'.format(self._qemu_opt.get('serial_port'))
+ cmd = "fuser -k {}/tcp".format(self._qemu_opt.get("serial_port"))
self._ssh.exec_command_sudo(cmd)
# Delete all created sockets
for sock in self._socks:
- cmd = 'rm -f {}'.format(sock)
+ cmd = "rm -f {}".format(sock)
self._ssh.exec_command_sudo(cmd)
def qemu_system_status(self):
@@ -659,15 +702,16 @@ class QemuUtils(object):
:return: VM status.
:rtype: str
"""
- out = self._qemu_qmp_exec('query-status')
- ret = out.get('return')
+ out = self._qemu_qmp_exec("query-status")
+ ret = out.get("return")
if ret is not None:
- return ret.get('status')
+ return ret.get("status")
else:
- err = out.get('error')
+ err = out.get("error")
raise RuntimeError(
- 'QEMU query-status failed on {0}, '
- 'error: {1}'.format(self._node['host'], json.dumps(err)))
+ "QEMU query-status failed on {0}, "
+ "error: {1}".format(self._node["host"], json.dumps(err))
+ )
@staticmethod
def build_qemu(node, force_install=False, apply_patch=False):
@@ -682,17 +726,23 @@ class QemuUtils(object):
:raises: RuntimeError if building QEMU failed.
"""
- directory = ' --directory={0}'.format(Constants.QEMU_INSTALL_DIR)
- version = ' --version={0}'.format(Constants.QEMU_INSTALL_VERSION)
- force = ' --force' if force_install else ''
- patch = ' --patch' if apply_patch else ''
-
- (ret_code, stdout, stderr) = VPPUtil. \
- exec_command(
- "sudo -E sh -c '{0}/{1}/qemu_build.sh{2}{3}{4}{5}'".
- format(Constants.REMOTE_FW_DIR, Constants.RESOURCES_LIB_SH,
- version, directory, force, patch), 1000)
+ directory = " --directory={0}".format(Constants.QEMU_INSTALL_DIR)
+ version = " --version={0}".format(Constants.QEMU_INSTALL_VERSION)
+ force = " --force" if force_install else ""
+ patch = " --patch" if apply_patch else ""
+
+ (ret_code, stdout, stderr) = VPPUtil.exec_command(
+ "sudo -E sh -c '{0}/{1}/qemu_build.sh{2}{3}{4}{5}'".format(
+ Constants.REMOTE_FW_DIR,
+ Constants.RESOURCES_LIB_SH,
+ version,
+ directory,
+ force,
+ patch,
+ ),
+ 1000,
+ )
if int(ret_code) != 0:
- logging.debug('QEMU build failed {0}'.format(stdout + stderr))
- raise RuntimeError('QEMU build failed on {0}'.format(node['host']))
+ logging.debug("QEMU build failed {0}".format(stdout + stderr))
+ raise RuntimeError("QEMU build failed on {0}".format(node["host"]))
diff --git a/extras/vpp_config/vpplib/VPPUtil.py b/extras/vpp_config/vpplib/VPPUtil.py
index 97747a31ca5..711f1032d96 100644
--- a/extras/vpp_config/vpplib/VPPUtil.py
+++ b/extras/vpp_config/vpplib/VPPUtil.py
@@ -23,15 +23,53 @@ from collections import Counter
import distro
-ubuntu_pkgs = {'release': ['vpp', 'vpp-plugin-core', 'vpp-plugin-dpdk', 'vpp-api-python', 'python3-vpp-api',
- 'vpp-dbg', 'vpp-dev', 'vpp-ext-deps'],
- 'master': ['vpp', 'vpp-plugin-core', 'vpp-plugin-dpdk', 'vpp-api-python', 'python3-vpp-api',
- 'vpp-dbg', 'vpp-dev', 'vpp-ext-deps']}
-
-centos_pkgs = {'release': ['vpp', 'vpp-selinux-policy', 'vpp-plugins', 'vpp-api-lua',
- 'vpp-api-python', 'vpp-debuginfo', 'vpp-devel', 'libvpp0', 'vpp-ext-deps'],
- 'master': ['vpp', 'vpp-selinux-policy', 'vpp-plugins', 'vpp-api-lua',
- 'vpp-api-python', 'vpp-debuginfo', 'vpp-devel', 'libvpp0', 'vpp-ext-deps']}
+ubuntu_pkgs = {
+ "release": [
+ "vpp",
+ "vpp-plugin-core",
+ "vpp-plugin-dpdk",
+ "vpp-api-python",
+ "python3-vpp-api",
+ "vpp-dbg",
+ "vpp-dev",
+ "vpp-ext-deps",
+ ],
+ "master": [
+ "vpp",
+ "vpp-plugin-core",
+ "vpp-plugin-dpdk",
+ "vpp-api-python",
+ "python3-vpp-api",
+ "vpp-dbg",
+ "vpp-dev",
+ "vpp-ext-deps",
+ ],
+}
+
+centos_pkgs = {
+ "release": [
+ "vpp",
+ "vpp-selinux-policy",
+ "vpp-plugins",
+ "vpp-api-lua",
+ "vpp-api-python",
+ "vpp-debuginfo",
+ "vpp-devel",
+ "libvpp0",
+ "vpp-ext-deps",
+ ],
+ "master": [
+ "vpp",
+ "vpp-selinux-policy",
+ "vpp-plugins",
+ "vpp-api-lua",
+ "vpp-api-python",
+ "vpp-debuginfo",
+ "vpp-devel",
+ "libvpp0",
+ "vpp-ext-deps",
+ ],
+}
class VPPUtil(object):
@@ -50,19 +88,23 @@ class VPPUtil(object):
"""
logging.info(" Local Command: {}".format(cmd))
- out = ''
- err = ''
- prc = subprocess.Popen(cmd, shell=True, bufsize=1,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ out = ""
+ err = ""
+ prc = subprocess.Popen(
+ cmd,
+ shell=True,
+ bufsize=1,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
with prc.stdout:
lines = prc.stdout.readlines()
for line in lines:
if type(line) != str:
line = line.decode()
- logging.info(" {}".format(line.strip('\n')))
+ logging.info(" {}".format(line.strip("\n")))
out += line
with prc.stderr:
@@ -70,7 +112,7 @@ class VPPUtil(object):
for line in lines:
if type(line) != str:
line = line.decode()
- logging.warning(" {}".format(line.strip('\n')))
+ logging.warning(" {}".format(line.strip("\n")))
err += line
ret = prc.wait()
@@ -86,17 +128,17 @@ class VPPUtil(object):
"""
# Does a copy of the file exist, if not create one
- ofile = filename + '.orig'
- (ret, stdout, stderr) = self.exec_command('ls {}'.format(ofile))
+ ofile = filename + ".orig"
+ (ret, stdout, stderr) = self.exec_command("ls {}".format(ofile))
if ret != 0:
logging.debug(stderr)
- if stdout.strip('\n') != ofile:
- cmd = 'sudo cp {} {}'.format(filename, ofile)
+ if stdout.strip("\n") != ofile:
+ cmd = "sudo cp {} {}".format(filename, ofile)
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
logging.debug(stderr)
- def _install_vpp_ubuntu(self, node, branch, ubuntu_version='xenial'):
+ def _install_vpp_ubuntu(self, node, branch, ubuntu_version="xenial"):
"""
Install the VPP packages
@@ -109,49 +151,49 @@ class VPPUtil(object):
"""
# Modify the sources list
- sfile = '/etc/apt/sources.list.d/99fd.io.list'
+ sfile = "/etc/apt/sources.list.d/99fd.io.list"
# Backup the sources list
self._autoconfig_backup_file(sfile)
- reps = 'deb [trusted=yes] https://packagecloud.io/fdio/'
- reps += '{}/ubuntu {} main\n'.format(branch, ubuntu_version)
+ reps = "deb [trusted=yes] https://packagecloud.io/fdio/"
+ reps += "{}/ubuntu {} main\n".format(branch, ubuntu_version)
- with open(sfile, 'w') as sfd:
+ with open(sfile, "w") as sfd:
sfd.write(reps)
sfd.close()
# Add the key
- key = requests.get(
- 'https://packagecloud.io/fdio/{}/gpgkey'.format(branch))
+ key = requests.get("https://packagecloud.io/fdio/{}/gpgkey".format(branch))
cmd = 'echo "{}" | apt-key add -'.format(key.content.decode(key.encoding))
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ raise RuntimeError(
+ "{} failed on node {} {}".format(cmd, node["host"], stderr)
+ )
# Install the package
- cmd = 'apt-get -y update'
+ cmd = "apt-get -y update"
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} apt-get update failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ raise RuntimeError(
+ "{} apt-get update failed on node {} {}".format(
+ cmd, node["host"], stderr
+ )
+ )
# Get the package list
- pkgstr = ''
+ pkgstr = ""
for ps in ubuntu_pkgs[branch]:
- pkgstr += ps + ' '
+ pkgstr += ps + " "
- cmd = 'apt-get -y install {}'.format(pkgstr)
+ cmd = "apt-get -y install {}".format(pkgstr)
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.format(
- cmd, node['host'], stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
def _install_vpp_centos(self, node, branch):
"""
@@ -164,95 +206,82 @@ class VPPUtil(object):
"""
# Be sure the correct system packages are installed
- cmd = 'yum -y update'
+ cmd = "yum -y update"
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
- cmd = 'yum -y install pygpgme yum-utils'
+ cmd = "yum -y install pygpgme yum-utils"
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
# Modify the sources list
- sfile = '/etc/yum.repos.d/fdio-release.repo'
+ sfile = "/etc/yum.repos.d/fdio-release.repo"
# Backup the sources list
self._autoconfig_backup_file(sfile)
# Remove the current file
- cmd = 'rm {}'.format(sfile)
+ cmd = "rm {}".format(sfile)
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
# Get the file contents
- reps = '\n'.join([
- '[fdio_{}]'.format(branch),
- 'name=fdio_{}'.format(branch),
- 'baseurl=https://packagecloud.io/fdio/{}/el/7/$basearch'.format(
- branch),
- 'repo_gpgcheck=1',
- 'gpgcheck=0',
- 'enabled=1',
- 'gpgkey=https://packagecloud.io/fdio/{}/gpgkey'.format(branch),
- 'sslverify=1',
- 'sslcacert=/etc/pki/tls/certs/ca-bundle.crt',
- 'metadata_expire=300\n',
- '[fdio_{}-source]'.format(branch),
- 'name=fdio_release-{}'.format(branch),
- 'baseurl=https://packagecloud.io/fdio/{}/el/7/SRPMS'.format(
- branch),
- 'repo_gpgcheck=1',
- 'gpgcheck=0',
- 'enabled=1',
- 'gpgkey=https://packagecloud.io/fdio/{}/gpgkey'.format(branch),
- 'sslverify =1',
- 'sslcacert=/etc/pki/tls/certs/ca-bundle.crt',
- 'metadata_expire=300\n'
- ])
- with open(sfile, 'w') as sfd:
+ reps = "\n".join(
+ [
+ "[fdio_{}]".format(branch),
+ "name=fdio_{}".format(branch),
+ "baseurl=https://packagecloud.io/fdio/{}/el/7/$basearch".format(branch),
+ "repo_gpgcheck=1",
+ "gpgcheck=0",
+ "enabled=1",
+ "gpgkey=https://packagecloud.io/fdio/{}/gpgkey".format(branch),
+ "sslverify=1",
+ "sslcacert=/etc/pki/tls/certs/ca-bundle.crt",
+ "metadata_expire=300\n",
+ "[fdio_{}-source]".format(branch),
+ "name=fdio_release-{}".format(branch),
+ "baseurl=https://packagecloud.io/fdio/{}/el/7/SRPMS".format(branch),
+ "repo_gpgcheck=1",
+ "gpgcheck=0",
+ "enabled=1",
+ "gpgkey=https://packagecloud.io/fdio/{}/gpgkey".format(branch),
+ "sslverify =1",
+ "sslcacert=/etc/pki/tls/certs/ca-bundle.crt",
+ "metadata_expire=300\n",
+ ]
+ )
+ with open(sfile, "w") as sfd:
sfd.write(reps)
sfd.close()
# Update the fdio repo
- cmd = 'yum clean all'
+ cmd = "yum clean all"
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
- cmd = "yum -q makecache -y --disablerepo='*' " \
- "--enablerepo='fdio_{}'".format(branch)
+ cmd = "yum -q makecache -y --disablerepo='*' " "--enablerepo='fdio_{}'".format(
+ branch
+ )
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
# Get the package list
- pkgstr = ''
+ pkgstr = ""
for ps in centos_pkgs[branch]:
- pkgstr += ps + ' '
+ pkgstr += ps + " "
- cmd = 'yum -y install {}'.format(pkgstr)
+ cmd = "yum -y install {}".format(pkgstr)
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.format(
- cmd, node['host'], stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
def install_vpp(self, node, branch):
"""
@@ -266,10 +295,10 @@ class VPPUtil(object):
"""
distro = self.get_linux_distro()
logging.info(" {}".format(distro[0]))
- if distro[0] == 'Ubuntu':
+ if distro[0] == "Ubuntu":
logging.info("Install Ubuntu")
self._install_vpp_ubuntu(node, branch, ubuntu_version=distro[2])
- elif distro[0] == 'CentOS Linux':
+ elif distro[0] == "CentOS Linux":
logging.info("Install CentOS")
self._install_vpp_centos(node, branch)
else:
@@ -286,17 +315,18 @@ class VPPUtil(object):
"""
# get the package list
- pkgstr = ''
+ pkgstr = ""
pkgs = self.get_installed_vpp_pkgs()
for pkg in pkgs:
- pkgname = pkg['name']
- pkgstr += pkgname + ' '
+ pkgname = pkg["name"]
+ pkgstr += pkgname + " "
- cmd = 'dpkg --purge {}'.format(pkgstr)
+ cmd = "dpkg --purge {}".format(pkgstr)
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.format(
- cmd, node['host'], stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
def _uninstall_vpp_centos(self, node):
"""
@@ -306,18 +336,19 @@ class VPPUtil(object):
:type node: dict
"""
- pkgstr = ''
+ pkgstr = ""
pkgs = self.get_installed_vpp_pkgs()
for pkg in pkgs:
- pkgname = pkg['name']
- pkgstr += pkgname + ' '
+ pkgname = pkg["name"]
+ pkgstr += pkgname + " "
logging.info("Uninstalling {}".format(pkgstr))
- cmd = 'yum -y remove {}'.format(pkgstr)
+ cmd = "yum -y remove {}".format(pkgstr)
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.format(
- cmd, node['host'], stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
def uninstall_vpp(self, node):
"""
@@ -330,10 +361,10 @@ class VPPUtil(object):
# First stop VPP
self.stop(node)
distro = self.get_linux_distro()
- if distro[0] == 'Ubuntu':
+ if distro[0] == "Ubuntu":
logging.info("Uninstall Ubuntu")
self._uninstall_vpp_ubuntu(node)
- elif distro[0] == 'CentOS Linux':
+ elif distro[0] == "CentOS Linux":
logging.info("Uninstall CentOS")
self._uninstall_vpp_centos(node)
else:
@@ -352,21 +383,20 @@ class VPPUtil(object):
:type additional_cmds: tuple
"""
def_setting_tb_displayed = {
- 'IPv6 FIB': 'ip6 fib',
- 'IPv4 FIB': 'ip fib',
- 'Interface IP': 'int addr',
- 'Interfaces': 'int',
- 'ARP': 'ip arp',
- 'Errors': 'err'
+ "IPv6 FIB": "ip6 fib",
+ "IPv4 FIB": "ip fib",
+ "Interface IP": "int addr",
+ "Interfaces": "int",
+ "ARP": "ip arp",
+ "Errors": "err",
}
if additional_cmds:
for cmd in additional_cmds:
- def_setting_tb_displayed['Custom Setting: {}'.format(cmd)] \
- = cmd
+ def_setting_tb_displayed["Custom Setting: {}".format(cmd)] = cmd
for _, value in def_setting_tb_displayed.items():
- self.exec_command('vppctl sh {}'.format(value))
+ self.exec_command("vppctl sh {}".format(value))
@staticmethod
def get_vms(node):
@@ -397,32 +427,32 @@ class VPPUtil(object):
:rtype: dictionary
"""
interfaces = {}
- cmd = 'vppctl show int addr'
+ cmd = "vppctl show int addr"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
return interfaces
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
if len(lines[0]) != 0:
- if lines[0].split(' ')[0] == 'FileNotFoundError':
+ if lines[0].split(" ")[0] == "FileNotFoundError":
return interfaces
- name = ''
+ name = ""
for line in lines:
if len(line) == 0:
continue
# If the first character is not whitespace
# create a new interface
- if len(re.findall(r'\s', line[0])) == 0:
+ if len(re.findall(r"\s", line[0])) == 0:
spl = line.split()
name = spl[0]
- if name == 'local0':
+ if name == "local0":
continue
interfaces[name] = {}
- interfaces[name]['state'] = spl[1].lstrip('(').rstrip('):\r')
+ interfaces[name]["state"] = spl[1].lstrip("(").rstrip("):\r")
else:
- interfaces[name]['address'] = line.lstrip(' ').rstrip('\r')
+ interfaces[name]["address"] = line.lstrip(" ").rstrip("\r")
return interfaces
@@ -439,14 +469,14 @@ class VPPUtil(object):
"""
interfaces = {}
- cmd = 'vppctl show hard'
+ cmd = "vppctl show hard"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
return interfaces
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
if len(lines[0]) != 0:
- if lines[0].split(' ')[0] == 'FileNotFoundError':
+ if lines[0].split(" ")[0] == "FileNotFoundError":
return interfaces
for line in lines:
@@ -455,46 +485,46 @@ class VPPUtil(object):
# If the first character is not whitespace
# create a new interface
- if len(re.findall(r'\s', line[0])) == 0:
+ if len(re.findall(r"\s", line[0])) == 0:
spl = line.split()
name = spl[0]
interfaces[name] = {}
- interfaces[name]['index'] = spl[1]
- interfaces[name]['state'] = spl[2]
+ interfaces[name]["index"] = spl[1]
+ interfaces[name]["state"] = spl[2]
# Ethernet address
- rfall = re.findall(r'Ethernet address', line)
+ rfall = re.findall(r"Ethernet address", line)
if rfall:
spl = line.split()
- interfaces[name]['mac'] = spl[2]
+ interfaces[name]["mac"] = spl[2]
# Carrier
- rfall = re.findall(r'carrier', line)
+ rfall = re.findall(r"carrier", line)
if rfall:
- spl = line.split('carrier ')
- interfaces[name]['carrier'] = spl[1]
+ spl = line.split("carrier ")
+ interfaces[name]["carrier"] = spl[1]
# Socket
- spl = ''
- rfall = re.findall(r'numa \d+', line)
+ spl = ""
+ rfall = re.findall(r"numa \d+", line)
if rfall:
spl = rfall[0].split()
- interfaces[name]['numa'] = rfall[0].split()[1]
+ interfaces[name]["numa"] = rfall[0].split()[1]
# Queues and Descriptors
- rfall = re.findall(r'rx\: queues \d+', line)
+ rfall = re.findall(r"rx\: queues \d+", line)
if rfall:
- interfaces[name]['rx queues'] = rfall[0].split()[2]
- rdesc = re.findall(r'desc \d+', line)
+ interfaces[name]["rx queues"] = rfall[0].split()[2]
+ rdesc = re.findall(r"desc \d+", line)
if rdesc:
- interfaces[name]['rx descs'] = rdesc[0].split()[1]
+ interfaces[name]["rx descs"] = rdesc[0].split()[1]
- rfall = re.findall(r'tx\: queues \d+', line)
+ rfall = re.findall(r"tx\: queues \d+", line)
if rfall:
- interfaces[name]['tx queues'] = rfall[0].split()[2]
- rdesc = re.findall(r'desc \d+', line)
+ interfaces[name]["tx queues"] = rfall[0].split()[2]
+ rdesc = re.findall(r"desc \d+", line)
if rdesc:
- interfaces[name]['tx descs'] = rdesc[0].split()[1]
+ interfaces[name]["tx descs"] = rdesc[0].split()[1]
return interfaces
@@ -508,17 +538,17 @@ class VPPUtil(object):
"""
pkgs = []
- cmd = 'dpkg -l | grep vpp'
+ cmd = "dpkg -l | grep vpp"
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
return pkgs
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
for line in lines:
items = line.split()
if len(items) < 2:
continue
- pkg = {'name': items[1], 'version': items[2]}
+ pkg = {"name": items[1], "version": items[2]}
pkgs.append(pkg)
return pkgs
@@ -533,21 +563,21 @@ class VPPUtil(object):
"""
pkgs = []
- cmd = 'rpm -qa | grep vpp'
+ cmd = "rpm -qa | grep vpp"
(ret, stdout, stderr) = self.exec_command(cmd)
if ret != 0:
return pkgs
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
for line in lines:
if len(line) == 0:
continue
items = line.split()
if len(items) < 2:
- pkg = {'name': items[0]}
+ pkg = {"name": items[0]}
else:
- pkg = {'name': items[1], 'version': items[2]}
+ pkg = {"name": items[1], "version": items[2]}
pkgs.append(pkg)
@@ -563,9 +593,9 @@ class VPPUtil(object):
"""
distro = self.get_linux_distro()
- if distro[0] == 'Ubuntu':
+ if distro[0] == "Ubuntu":
pkgs = self._get_installed_vpp_pkgs_ubuntu()
- elif distro[0] == 'CentOS Linux':
+ elif distro[0] == "CentOS Linux":
pkgs = self._get_installed_vpp_pkgs_centos()
else:
pkgs = self._get_installed_vpp_pkgs_centos()
@@ -594,7 +624,7 @@ class VPPUtil(object):
numa_list = []
for if_key in iface_keys:
try:
- numa_list.append(node['interfaces'][if_key].get('numa_node'))
+ numa_list.append(node["interfaces"][if_key].get("numa_node"))
except KeyError:
pass
@@ -617,12 +647,12 @@ class VPPUtil(object):
:type node: dict
"""
- cmd = 'service vpp restart'
+ cmd = "service vpp restart"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'],
- stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
@staticmethod
def start(node):
@@ -634,12 +664,12 @@ class VPPUtil(object):
:type node: dict
"""
- cmd = 'service vpp start'
+ cmd = "service vpp start"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'],
- stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
@staticmethod
def stop(node):
@@ -651,12 +681,12 @@ class VPPUtil(object):
:type node: dict
"""
- cmd = 'service vpp stop'
+ cmd = "service vpp stop"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- logging.debug('{} failed on node {} {} {}'.
- format(cmd, node['host'],
- stdout, stderr))
+ logging.debug(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
# noinspection RegExpRedundantEscape
@staticmethod
@@ -676,11 +706,11 @@ class VPPUtil(object):
if len(pkgs) == 0:
return "Not Installed", errors
- cmd = 'service vpp status'
+ cmd = "service vpp status"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
# Get the active status
- state = re.findall(r'Active:[\w (\)]+', stdout)[0].split(' ')
+ state = re.findall(r"Active:[\w (\)]+", stdout)[0].split(" ")
if len(state) > 2:
statestr = "{} {}".format(state[1], state[2])
else:
@@ -707,13 +737,10 @@ class VPPUtil(object):
"""
dist = distro.linux_distribution()
- if dist[0] == 'Ubuntu' or \
- dist[0] == 'CentOS Linux' or \
- dist[:7] == 'Red Hat':
+ if dist[0] == "Ubuntu" or dist[0] == "CentOS Linux" or dist[:7] == "Red Hat":
return dist
else:
- raise RuntimeError(
- 'Linux Distribution {} is not supported'.format(dist[0]))
+ raise RuntimeError("Linux Distribution {} is not supported".format(dist[0]))
@staticmethod
def version():
@@ -726,21 +753,21 @@ class VPPUtil(object):
"""
version = {}
- cmd = 'vppctl show version verbose'
+ cmd = "vppctl show version verbose"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
return version
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
if len(lines[0]) != 0:
- if lines[0].split(' ')[0] == 'FileNotFoundError':
+ if lines[0].split(" ")[0] == "FileNotFoundError":
return version
for line in lines:
if len(line) == 0:
continue
- dct = line.split(':')
- version[dct[0]] = dct[1].lstrip(' ')
+ dct = line.split(":")
+ version[dct[0]] = dct[1].lstrip(" ")
return version
@@ -755,38 +782,40 @@ class VPPUtil(object):
"""
ifaces = []
- cmd = 'vppctl show bridge'
+ cmd = "vppctl show bridge"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'],
- stdout, stderr))
- lines = stdout.split('\r\n')
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
+ lines = stdout.split("\r\n")
bridges = []
for line in lines:
- if line == 'no bridge-domains in use':
+ if line == "no bridge-domains in use":
print(line)
return ifaces
if len(line) == 0:
continue
- lspl = line.lstrip(' ').split()
- if lspl[0] != 'BD-ID':
+ lspl = line.lstrip(" ").split()
+ if lspl[0] != "BD-ID":
bridges.append(lspl[0])
for bridge in bridges:
- cmd = 'vppctl show bridge {} detail'.format(bridge)
+ cmd = "vppctl show bridge {} detail".format(bridge)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'],
- stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(
+ cmd, node["host"], stdout, stderr
+ )
+ )
- lines = stdout.split('\r\n')
+ lines = stdout.split("\r\n")
for line in lines:
- iface = re.findall(r'[a-zA-z]+\d+/\d+/\d+', line)
+ iface = re.findall(r"[a-zA-z]+\d+/\d+/\d+", line)
if len(iface):
- ifcidx = {'name': iface[0], 'index': line.split()[1]}
+ ifcidx = {"name": iface[0], "index": line.split()[1]}
ifaces.append(ifcidx)
print(stdout)
diff --git a/extras/vpp_config/vpplib/VppGrubUtil.py b/extras/vpp_config/vpplib/VppGrubUtil.py
index f17efd8a868..976b20019c4 100644
--- a/extras/vpp_config/vpplib/VppGrubUtil.py
+++ b/extras/vpp_config/vpplib/VppGrubUtil.py
@@ -17,11 +17,11 @@ import re
from vpplib.VPPUtil import VPPUtil
-__all__ = ['VppGrubUtil']
+__all__ = ["VppGrubUtil"]
class VppGrubUtil(object):
- """ VPP Grub Utilities."""
+ """VPP Grub Utilities."""
def _get_current_cmdline(self):
"""
@@ -32,14 +32,14 @@ class VppGrubUtil(object):
"""
# Get the memory information using /proc/meminfo
- cmd = 'sudo cat /proc/cmdline'
+ cmd = "sudo cat /proc/cmdline"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} on node {} {} {}'.
- format(cmd, self._node['host'],
- stdout, stderr))
+ raise RuntimeError(
+ "{} on node {} {} {}".format(cmd, self._node["host"], stdout, stderr)
+ )
- self._current_cmdline = stdout.strip('\n')
+ self._current_cmdline = stdout.strip("\n")
def _get_default_cmdline(self):
"""
@@ -50,21 +50,24 @@ class VppGrubUtil(object):
"""
# Get the default grub cmdline
- rootdir = self._node['rootdir']
- gfile = self._node['cpu']['grub_config_file']
- grubcmdline = self._node['cpu']['grubcmdline']
- cmd = 'cat {}'.format(rootdir + gfile)
+ rootdir = self._node["rootdir"]
+ gfile = self._node["cpu"]["grub_config_file"]
+ grubcmdline = self._node["cpu"]["grubcmdline"]
+ cmd = "cat {}".format(rootdir + gfile)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} Executing failed on node {} {}'.
- format(cmd, self._node['host'], stderr))
+ raise RuntimeError(
+ "{} Executing failed on node {} {}".format(
+ cmd, self._node["host"], stderr
+ )
+ )
# Get the Default Linux command line, ignoring commented lines
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
for line in lines:
- if line == '' or line[0] == '#':
+ if line == "" or line[0] == "#":
continue
- ldefault = re.findall(r'{}=.+'.format(grubcmdline), line)
+ ldefault = re.findall(r"{}=.+".format(grubcmdline), line)
if ldefault:
self._default_cmdline = ldefault[0]
break
@@ -96,9 +99,9 @@ class VppGrubUtil(object):
:returns: The command line
:rtype: string
"""
- grubcmdline = self._node['cpu']['grubcmdline']
+ grubcmdline = self._node["cpu"]["grubcmdline"]
cmdline = self._default_cmdline
- value = cmdline.split('{}='.format(grubcmdline))[1]
+ value = cmdline.split("{}=".format(grubcmdline))[1]
value = value.rstrip('"').lstrip('"')
# jadfix intel_pstate=disable sometimes cause networks to
@@ -111,43 +114,43 @@ class VppGrubUtil(object):
# value = '{} intel_pstate=disable'.format(value)
# Replace isolcpus with ours
- isolcpus = re.findall(r'isolcpus=[\w+\-,]+', value)
+ isolcpus = re.findall(r"isolcpus=[\w+\-,]+", value)
if not isolcpus:
- if isolated_cpus != '':
+ if isolated_cpus != "":
value = "{} isolcpus={}".format(value, isolated_cpus)
else:
- if isolated_cpus != '':
- value = re.sub(r'isolcpus=[\w+\-,]+',
- 'isolcpus={}'.format(isolated_cpus),
- value)
+ if isolated_cpus != "":
+ value = re.sub(
+ r"isolcpus=[\w+\-,]+", "isolcpus={}".format(isolated_cpus), value
+ )
else:
- value = re.sub(r'isolcpus=[\w+\-,]+', '', value)
+ value = re.sub(r"isolcpus=[\w+\-,]+", "", value)
- nohz = re.findall(r'nohz_full=[\w+\-,]+', value)
+ nohz = re.findall(r"nohz_full=[\w+\-,]+", value)
if not nohz:
- if isolated_cpus != '':
+ if isolated_cpus != "":
value = "{} nohz_full={}".format(value, isolated_cpus)
else:
- if isolated_cpus != '':
- value = re.sub(r'nohz_full=[\w+\-,]+',
- 'nohz_full={}'.format(isolated_cpus),
- value)
+ if isolated_cpus != "":
+ value = re.sub(
+ r"nohz_full=[\w+\-,]+", "nohz_full={}".format(isolated_cpus), value
+ )
else:
- value = re.sub(r'nohz_full=[\w+\-,]+', '', value)
+ value = re.sub(r"nohz_full=[\w+\-,]+", "", value)
- rcu = re.findall(r'rcu_nocbs=[\w+\-,]+', value)
+ rcu = re.findall(r"rcu_nocbs=[\w+\-,]+", value)
if not rcu:
- if isolated_cpus != '':
+ if isolated_cpus != "":
value = "{} rcu_nocbs={}".format(value, isolated_cpus)
else:
- if isolated_cpus != '':
- value = re.sub(r'rcu_nocbs=[\w+\-,]+',
- 'rcu_nocbs={}'.format(isolated_cpus),
- value)
+ if isolated_cpus != "":
+ value = re.sub(
+ r"rcu_nocbs=[\w+\-,]+", "rcu_nocbs={}".format(isolated_cpus), value
+ )
else:
- value = re.sub(r'rcu_nocbs=[\w+\-,]+', '', value)
+ value = re.sub(r"rcu_nocbs=[\w+\-,]+", "", value)
- value = value.lstrip(' ').rstrip(' ')
+ value = value.lstrip(" ").rstrip(" ")
cmdline = '{}="{}"'.format(grubcmdline, value)
return cmdline
@@ -167,69 +170,68 @@ class VppGrubUtil(object):
if len(vpp_cmdline):
# Update grub
# Save the original file
- rootdir = node['rootdir']
- grubcmdline = node['cpu']['grubcmdline']
- ofilename = rootdir + node['cpu']['grub_config_file'] + '.orig'
- filename = rootdir + node['cpu']['grub_config_file']
+ rootdir = node["rootdir"]
+ grubcmdline = node["cpu"]["grubcmdline"]
+ ofilename = rootdir + node["cpu"]["grub_config_file"] + ".orig"
+ filename = rootdir + node["cpu"]["grub_config_file"]
# Write the output file
# Does a copy of the original file exist, if not create one
- (ret, stdout, stderr) = VPPUtil.exec_command(
- 'ls {}'.format(ofilename))
+ (ret, stdout, stderr) = VPPUtil.exec_command("ls {}".format(ofilename))
if ret != 0:
- if stdout.strip('\n') != ofilename:
- cmd = 'sudo cp {} {}'.format(filename, ofilename)
+ if stdout.strip("\n") != ofilename:
+ cmd = "sudo cp {} {}".format(filename, ofilename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.
- format(cmd, self._node['host'],
- stderr))
+ raise RuntimeError(
+ "{} failed on node {} {}".format(
+ cmd, self._node["host"], stderr
+ )
+ )
# Get the contents of the current grub config file
- cmd = 'cat {}'.format(filename)
+ cmd = "cat {}".format(filename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.format(
- cmd,
- self._node['host'],
- stderr))
+ raise RuntimeError(
+ "{} failed on node {} {}".format(cmd, self._node["host"], stderr)
+ )
# Write the new contents
# Get the Default Linux command line, ignoring commented lines
content = ""
- lines = stdout.split('\n')
+ lines = stdout.split("\n")
for line in lines:
- if line == '':
- content += line + '\n'
+ if line == "":
+ content += line + "\n"
continue
- if line[0] == '#':
- content += line + '\n'
+ if line[0] == "#":
+ content += line + "\n"
continue
- ldefault = re.findall(r'{}=.+'.format(grubcmdline), line)
+ ldefault = re.findall(r"{}=.+".format(grubcmdline), line)
if ldefault:
- content += vpp_cmdline + '\n'
+ content += vpp_cmdline + "\n"
else:
- content += line + '\n'
+ content += line + "\n"
content = content.replace(r"`", r"\`")
- content = content.rstrip('\n')
+ content = content.rstrip("\n")
cmd = "sudo cat > {0} << EOF\n{1}\n".format(filename, content)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.format(
- cmd,
- self._node['host'],
- stderr))
+ raise RuntimeError(
+ "{} failed on node {} {}".format(cmd, self._node["host"], stderr)
+ )
return vpp_cmdline
def __init__(self, node):
distro = VPPUtil.get_linux_distro()
- if distro[0] == 'Ubuntu':
- node['cpu']['grubcmdline'] = 'GRUB_CMDLINE_LINUX_DEFAULT'
+ if distro[0] == "Ubuntu":
+ node["cpu"]["grubcmdline"] = "GRUB_CMDLINE_LINUX_DEFAULT"
else:
- node['cpu']['grubcmdline'] = 'GRUB_CMDLINE_LINUX'
+ node["cpu"]["grubcmdline"] = "GRUB_CMDLINE_LINUX"
self._node = node
self._current_cmdline = ""
diff --git a/extras/vpp_config/vpplib/VppHugePageUtil.py b/extras/vpp_config/vpplib/VppHugePageUtil.py
index 3a632828883..48991090f04 100644
--- a/extras/vpp_config/vpplib/VppHugePageUtil.py
+++ b/extras/vpp_config/vpplib/VppHugePageUtil.py
@@ -33,6 +33,7 @@ class VppHugePageUtil(object):
"""
Huge Page Utilities
"""
+
def hugepages_dryrun_apply(self):
"""
Apply the huge page configuration
@@ -40,23 +41,23 @@ class VppHugePageUtil(object):
"""
node = self._node
- hugepages = node['hugepages']
+ hugepages = node["hugepages"]
vpp_hugepage_config = VPP_HUGEPAGE_CONFIG.format(
- nr_hugepages=hugepages['total'],
- max_map_count=hugepages['max_map_count'],
- shmmax=hugepages['shmax'])
+ nr_hugepages=hugepages["total"],
+ max_map_count=hugepages["max_map_count"],
+ shmmax=hugepages["shmax"],
+ )
- rootdir = node['rootdir']
- filename = rootdir + node['hugepages']['hugepage_config_file']
+ rootdir = node["rootdir"]
+ filename = rootdir + node["hugepages"]["hugepage_config_file"]
- cmd = 'echo "{0}" | sudo tee {1}'.\
- format(vpp_hugepage_config, filename)
+ cmd = 'echo "{0}" | sudo tee {1}'.format(vpp_hugepage_config, filename)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.
- format(cmd, node['host'],
- stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
def get_actual_huge_pages(self):
"""
@@ -68,25 +69,26 @@ class VppHugePageUtil(object):
"""
# Get the memory information using /proc/meminfo
- cmd = 'sudo cat /proc/meminfo'
+ cmd = "sudo cat /proc/meminfo"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
raise RuntimeError(
- '{} failed on node {} {} {}'.format(
- cmd, self._node['host'],
- stdout, stderr))
-
- total = re.findall(r'HugePages_Total:\s+\w+', stdout)
- free = re.findall(r'HugePages_Free:\s+\w+', stdout)
- size = re.findall(r'Hugepagesize:\s+\w+\s+\w+', stdout)
- memtotal = re.findall(r'MemTotal:\s+\w+\s+\w+', stdout)
- memfree = re.findall(r'MemFree:\s+\w+\s+\w+', stdout)
-
- total = total[0].split(':')[1].lstrip()
- free = free[0].split(':')[1].lstrip()
- size = size[0].split(':')[1].lstrip()
- memtotal = memtotal[0].split(':')[1].lstrip()
- memfree = memfree[0].split(':')[1].lstrip()
+ "{} failed on node {} {} {}".format(
+ cmd, self._node["host"], stdout, stderr
+ )
+ )
+
+ total = re.findall(r"HugePages_Total:\s+\w+", stdout)
+ free = re.findall(r"HugePages_Free:\s+\w+", stdout)
+ size = re.findall(r"Hugepagesize:\s+\w+\s+\w+", stdout)
+ memtotal = re.findall(r"MemTotal:\s+\w+\s+\w+", stdout)
+ memfree = re.findall(r"MemFree:\s+\w+\s+\w+", stdout)
+
+ total = total[0].split(":")[1].lstrip()
+ free = free[0].split(":")[1].lstrip()
+ size = size[0].split(":")[1].lstrip()
+ memtotal = memtotal[0].split(":")[1].lstrip()
+ memfree = memfree[0].split(":")[1].lstrip()
return total, free, size, memtotal, memfree
def show_huge_pages(self):
@@ -96,17 +98,13 @@ class VppHugePageUtil(object):
"""
node = self._node
- hugepages = node['hugepages']
- print (" {:30}: {}".format("Total System Memory",
- hugepages['memtotal']))
- print (" {:30}: {}".format("Total Free Memory",
- hugepages['memfree']))
- print (" {:30}: {}".format("Actual Huge Page Total",
- hugepages['actual_total']))
- print (" {:30}: {}".format("Configured Huge Page Total",
- hugepages['total']))
- print (" {:30}: {}".format("Huge Pages Free", hugepages['free']))
- print (" {:30}: {}".format("Huge Page Size", hugepages['size']))
+ hugepages = node["hugepages"]
+ print(" {:30}: {}".format("Total System Memory", hugepages["memtotal"]))
+ print(" {:30}: {}".format("Total Free Memory", hugepages["memfree"]))
+ print(" {:30}: {}".format("Actual Huge Page Total", hugepages["actual_total"]))
+ print(" {:30}: {}".format("Configured Huge Page Total", hugepages["total"]))
+ print(" {:30}: {}".format("Huge Pages Free", hugepages["free"]))
+ print(" {:30}: {}".format("Huge Page Size", hugepages["size"]))
def get_huge_page_config(self):
"""
@@ -115,7 +113,7 @@ class VppHugePageUtil(object):
:returns: The map max count and shmmax
"""
- total = self._node['hugepages']['total']
+ total = self._node["hugepages"]["total"]
max_map_count = int(total) * 2 + 1024
shmmax = int(total) * 2 * 1024 * 1024
return max_map_count, shmmax
diff --git a/extras/vpp_config/vpplib/VppPCIUtil.py b/extras/vpp_config/vpplib/VppPCIUtil.py
index ceda46f97b9..032a262c21c 100644
--- a/extras/vpp_config/vpplib/VppPCIUtil.py
+++ b/extras/vpp_config/vpplib/VppPCIUtil.py
@@ -23,7 +23,7 @@ from vpplib.VPPUtil import VPPUtil
DPDK_SCRIPT = "/vpp/vpp-config/scripts/dpdk-devbind.py"
# PCI Device id regular expresssion
-PCI_DEV_ID_REGEX = '[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+.[0-9A-Fa-f]+'
+PCI_DEV_ID_REGEX = "[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+.[0-9A-Fa-f]+"
class VppPCIUtil(object):
@@ -45,51 +45,47 @@ class VppPCIUtil(object):
devices = {}
ids = re.findall(PCI_DEV_ID_REGEX, device_string)
- descriptions = re.findall(r'\'([\s\S]*?)\'', device_string)
- unused = re.findall(r'unused=\w+|unused=', device_string)
+ descriptions = re.findall(r"\'([\s\S]*?)\'", device_string)
+ unused = re.findall(r"unused=\w+|unused=", device_string)
for i, j in enumerate(ids):
- device = {'description': descriptions[i]}
+ device = {"description": descriptions[i]}
if unused:
- device['unused'] = unused[i].split('=')[1].split(',')
+ device["unused"] = unused[i].split("=")[1].split(",")
- cmd = 'ls /sys/bus/pci/devices/{}/driver/module/drivers'. \
- format(ids[i])
+ cmd = "ls /sys/bus/pci/devices/{}/driver/module/drivers".format(ids[i])
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret == 0:
- device['driver'] = stdout.split(':')[1].rstrip('\n')
+ device["driver"] = stdout.split(":")[1].rstrip("\n")
- cmd = 'cat /sys/bus/pci/devices/{}/numa_node'.format(ids[i])
+ cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(ids[i])
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed {} {}'.
- format(cmd, stderr, stdout))
- numa_node = stdout.rstrip('\n')
- if numa_node == '-1':
- device['numa_node'] = '0'
+ raise RuntimeError("{} failed {} {}".format(cmd, stderr, stdout))
+ numa_node = stdout.rstrip("\n")
+ if numa_node == "-1":
+ device["numa_node"] = "0"
else:
- device['numa_node'] = numa_node
+ device["numa_node"] = numa_node
interfaces = []
- device['interfaces'] = []
- cmd = 'ls /sys/bus/pci/devices/{}/net'.format(ids[i])
+ device["interfaces"] = []
+ cmd = "ls /sys/bus/pci/devices/{}/net".format(ids[i])
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret == 0:
- interfaces = stdout.rstrip('\n').split()
- device['interfaces'] = interfaces
+ interfaces = stdout.rstrip("\n").split()
+ device["interfaces"] = interfaces
l2_addrs = []
for intf in interfaces:
- cmd = 'cat /sys/bus/pci/devices/{}/net/{}/address'.format(
- ids[i], intf)
+ cmd = "cat /sys/bus/pci/devices/{}/net/{}/address".format(ids[i], intf)
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed {} {}'.
- format(cmd, stderr, stdout))
+ raise RuntimeError("{} failed {} {}".format(cmd, stderr, stdout))
- l2_addrs.append(stdout.rstrip('\n'))
+ l2_addrs.append(stdout.rstrip("\n"))
- device['l2addr'] = l2_addrs
+ device["l2addr"] = l2_addrs
devices[ids[i]] = device
@@ -112,66 +108,62 @@ class VppPCIUtil(object):
"""
node = self._node
- rootdir = node['rootdir']
+ rootdir = node["rootdir"]
dpdk_script = rootdir + DPDK_SCRIPT
- cmd = dpdk_script + ' --status'
+ cmd = dpdk_script + " --status"
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
+ raise RuntimeError(
+ "{} failed on node {} {}".format(cmd, node["host"], stderr)
+ )
# Get the network devices using the DPDK
# First get everything after using DPDK
- stda = stdout.split('Network devices using DPDK-compatible driver')[1]
+ stda = stdout.split("Network devices using DPDK-compatible driver")[1]
# Then get everything before using kernel driver
- using_dpdk = stda.split('Network devices using kernel driver')[0]
+ using_dpdk = stda.split("Network devices using kernel driver")[0]
self._dpdk_devices = self._create_device_list(using_dpdk)
# Get the network devices using the kernel
- stda = stdout.split('Network devices using kernel driver')[1]
- using_kernel = stda.split('Other network devices')[0]
+ stda = stdout.split("Network devices using kernel driver")[1]
+ using_kernel = stda.split("Other network devices")[0]
self._kernel_devices = self._create_device_list(using_kernel)
# Get the other network devices
- stda = stdout.split('Other network devices')[1]
- other = stda.split('Crypto devices using DPDK-compatible driver')[0]
+ stda = stdout.split("Other network devices")[1]
+ other = stda.split("Crypto devices using DPDK-compatible driver")[0]
self._other_devices = self._create_device_list(other)
# Get the crypto devices using the DPDK
- stda = stdout.split('Crypto devices using DPDK-compatible driver')[1]
- crypto_using_dpdk = stda.split('Crypto devices using kernel driver')[0]
- self._crypto_dpdk_devices = self._create_device_list(
- crypto_using_dpdk)
+ stda = stdout.split("Crypto devices using DPDK-compatible driver")[1]
+ crypto_using_dpdk = stda.split("Crypto devices using kernel driver")[0]
+ self._crypto_dpdk_devices = self._create_device_list(crypto_using_dpdk)
# Get the network devices using the kernel
- stda = stdout.split('Crypto devices using kernel driver')[1]
- crypto_using_kernel = stda.split('Other crypto devices')[0]
- self._crypto_kernel_devices = self._create_device_list(
- crypto_using_kernel)
+ stda = stdout.split("Crypto devices using kernel driver")[1]
+ crypto_using_kernel = stda.split("Other crypto devices")[0]
+ self._crypto_kernel_devices = self._create_device_list(crypto_using_kernel)
# Get the other network devices
- crypto_other = stdout.split('Other crypto devices')[1]
+ crypto_other = stdout.split("Other crypto devices")[1]
self._crypto_other_devices = self._create_device_list(crypto_other)
# Get the devices used by the kernel
for devk in self._kernel_devices.items():
dvid = devk[0]
device = devk[1]
- for i in device['interfaces']:
+ for i in device["interfaces"]:
cmd = "ip addr show " + i
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {}'.format(
- cmd,
- node['host'],
- stderr))
- lstate = re.findall(r'state \w+', stdout)[0].split(' ')[1]
+ raise RuntimeError(
+ "{} failed on node {} {}".format(cmd, node["host"], stderr)
+ )
+ lstate = re.findall(r"state \w+", stdout)[0].split(" ")[1]
# Take care of the links that are UP
- if lstate == 'UP':
- device['linkup'] = True
+ if lstate == "UP":
+ device["linkup"] = True
self._link_up_devices[dvid] = device
for devl in self._link_up_devices.items():
@@ -234,18 +226,18 @@ class VppPCIUtil(object):
"""
- name = 'port' + str(len(interfaces))
+ name = "port" + str(len(interfaces))
interfaces[name] = {}
- interfaces[name]['pci_address'] = device_id
- interfaces[name]['numa_node'] = device['numa_node']
- if 'l2addr' in device:
- l2_addrs = device['l2addr']
+ interfaces[name]["pci_address"] = device_id
+ interfaces[name]["numa_node"] = device["numa_node"]
+ if "l2addr" in device:
+ l2_addrs = device["l2addr"]
for i, j in enumerate(l2_addrs):
if i > 0:
- mname = 'mac_address' + str(i + 1)
+ mname = "mac_address" + str(i + 1)
interfaces[name][mname] = l2_addrs[i]
else:
- interfaces[name]['mac_address'] = l2_addrs[i]
+ interfaces[name]["mac_address"] = l2_addrs[i]
@staticmethod
def show_vpp_devices(devices, show_interfaces=True, show_header=True):
@@ -261,34 +253,33 @@ class VppPCIUtil(object):
"""
if show_interfaces:
- header = "{:15} {:25} {:50}".format("PCI ID",
- "Kernel Interface(s)",
- "Description")
+ header = "{:15} {:25} {:50}".format(
+ "PCI ID", "Kernel Interface(s)", "Description"
+ )
else:
- header = "{:15} {:50}".format("PCI ID",
- "Description")
- dashseparator = ("-" * (len(header) - 2))
+ header = "{:15} {:50}".format("PCI ID", "Description")
+ dashseparator = "-" * (len(header) - 2)
if show_header is True:
- print (header)
- print (dashseparator)
+ print(header)
+ print(dashseparator)
for dit in devices.items():
dvid = dit[0]
device = dit[1]
if show_interfaces:
- interfaces = device['interfaces']
- interface = ''
+ interfaces = device["interfaces"]
+ interface = ""
for i, j in enumerate(interfaces):
if i > 0:
- interface += ',' + interfaces[i]
+ interface += "," + interfaces[i]
else:
interface = interfaces[i]
- print ("{:15} {:25} {:50}".format(
- dvid, interface, device['description']))
+ print(
+ "{:15} {:25} {:50}".format(dvid, interface, device["description"])
+ )
else:
- print ("{:15} {:50}".format(
- dvid, device['description']))
+ print("{:15} {:50}".format(dvid, device["description"]))
@staticmethod
def unbind_vpp_device(node, device_id):
@@ -301,14 +292,14 @@ class VppPCIUtil(object):
:type device_id: string
"""
- rootdir = node['rootdir']
+ rootdir = node["rootdir"]
dpdk_script = rootdir + DPDK_SCRIPT
- cmd = dpdk_script + ' -u ' + ' ' + device_id
+ cmd = dpdk_script + " -u " + " " + device_id
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- raise RuntimeError('{} failed on node {} {} {}'.format(
- cmd, node['host'],
- stdout, stderr))
+ raise RuntimeError(
+ "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
+ )
@staticmethod
def bind_vpp_device(node, driver, device_id):
@@ -324,14 +315,14 @@ class VppPCIUtil(object):
:returns ret: Command return code
"""
- rootdir = node['rootdir']
+ rootdir = node["rootdir"]
dpdk_script = rootdir + DPDK_SCRIPT
- cmd = dpdk_script + ' -b ' + driver + ' ' + device_id
+ cmd = dpdk_script + " -b " + driver + " " + device_id
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
if ret != 0:
- logging.error('{} failed on node {}'.format(
- cmd, node['host'], stdout, stderr))
- logging.error('{} {}'.format(
- stdout, stderr))
+ logging.error(
+ "{} failed on node {}".format(cmd, node["host"], stdout, stderr)
+ )
+ logging.error("{} {}".format(stdout, stderr))
return ret
diff --git a/extras/vpp_config/vpplib/constants.py b/extras/vpp_config/vpplib/constants.py
index 051a21cf023..63428b0c4d4 100644
--- a/extras/vpp_config/vpplib/constants.py
+++ b/extras/vpp_config/vpplib/constants.py
@@ -18,31 +18,31 @@ class Constants(object):
"""Constants used in CSIT."""
# OpenVPP testing directory location at topology nodes
- REMOTE_FW_DIR = '/tmp/openvpp-testing'
+ REMOTE_FW_DIR = "/tmp/openvpp-testing"
# shell scripts location
- RESOURCES_LIB_SH = 'resources/libraries/bash'
+ RESOURCES_LIB_SH = "resources/libraries/bash"
# vat templates location
- RESOURCES_TPL_VAT = 'resources/templates/vat'
+ RESOURCES_TPL_VAT = "resources/templates/vat"
# OpenVPP VAT binary name
- VAT_BIN_NAME = 'vpp_api_test'
+ VAT_BIN_NAME = "vpp_api_test"
# QEMU version to install
- QEMU_INSTALL_VERSION = 'qemu-2.5.0'
+ QEMU_INSTALL_VERSION = "qemu-2.5.0"
# QEMU install directory
- QEMU_INSTALL_DIR = '/opt/qemu-2.5.0'
+ QEMU_INSTALL_DIR = "/opt/qemu-2.5.0"
# Honeycomb directory location at topology nodes:
- REMOTE_HC_DIR = '/opt/honeycomb'
+ REMOTE_HC_DIR = "/opt/honeycomb"
# Honeycomb persistence files location
- REMOTE_HC_PERSIST = '/var/lib/honeycomb/persist'
+ REMOTE_HC_PERSIST = "/var/lib/honeycomb/persist"
# Honeycomb templates location
- RESOURCES_TPL_HC = 'resources/templates/honeycomb'
+ RESOURCES_TPL_HC = "resources/templates/honeycomb"
# ODL Client Restconf listener port
ODL_PORT = 8181
diff --git a/extras/vpp_if_stats/README.md b/extras/vpp_if_stats/README.md
deleted file mode 100755
index 89d60c5255d..00000000000
--- a/extras/vpp_if_stats/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# VPP interface stats client {#if_stats_client_doc}
-
-This is a source code and a binary of a 'thin client' to collect,
-aggregate and expose VPP interface stats through VPP stats socket API.
-It also provides some information about the installed VPP version.
-
-This can be used by monitoring systems that needs to grab those details
-through a simple executable client with no dependencies.
-
-example use case: where VPP runs in a container that can't expose the socket API to the host level
-
-
-## Prerequisites (for building)
-
-**GoVPP** library (compatible with VPP 18.10)
-vpp, vpp-api, vpp-lib
-
-## Building
-
-```bash
-go get git.fd.io/govpp.git
-go build
-```
-
-## Using (post-build for example on linux 64bit)
-
-```bash
-./bin/vpp_if_stats_linux_amd64
-```
-
-## Output examples
-
-[JSON schema](./response_schema.json)
-[Example](./response_example.json)
-
diff --git a/extras/vpp_if_stats/README.rst b/extras/vpp_if_stats/README.rst
new file mode 100644
index 00000000000..2e4dcc0fb4d
--- /dev/null
+++ b/extras/vpp_if_stats/README.rst
@@ -0,0 +1,40 @@
+.. _if_stats_client_doc:
+
+VPP interface stats client
+==========================
+
+This is a source code and a binary of a ‘thin client’ to collect,
+aggregate and expose VPP interface stats through VPP stats socket API.
+It also provides some information about the installed VPP version.
+
+This can be used by monitoring systems that needs to grab those details
+through a simple executable client with no dependencies.
+
+example use case: where VPP runs in a container that can’t expose the
+socket API to the host level
+
+Prerequisites (for building)
+----------------------------
+
+**GoVPP** library (compatible with VPP 18.10) vpp, vpp-api, vpp-lib
+
+Building
+--------
+
+.. code:: bash
+
+ go get git.fd.io/govpp.git
+ go build
+
+Using (post-build for example on linux 64bit)
+---------------------------------------------
+
+.. code:: bash
+
+ ./bin/vpp_if_stats_linux_amd64
+
+Output examples
+---------------
+
+`JSON schema <./response_schema.json>`__
+`Example <./response_example.json>`__
diff --git a/extras/vpp_stats_fs/README.md b/extras/vpp_stats_fs/README.md
deleted file mode 100755
index 52610feba94..00000000000
--- a/extras/vpp_stats_fs/README.md
+++ /dev/null
@@ -1,113 +0,0 @@
-# VPP stats segment FUSE filesystem {#stats_fs_doc}
-
-The statfs binary allows to create a FUSE filesystem to expose and to browse the stats segment.
-It relies on the Go-FUSE library and requires Go-VPP stats bindings to work.
-
-The binary mounts a filesystem on the local machine whith the data from the stats segments.
-The counters can be opened and read as files (e.g. in a Unix shell).
-Note that the value of a counter is determined when the corresponding file is opened (as for /proc/interrupts).
-
-Directories update their contents on epoch changes so that new counters get added to the filesystem.
-
-The script `install.sh` is responsible for buildiing and installing the filesystem.
-
-## Usage
-
-The local Makefile contains targets for all the possible intercations with the stats_f binary.
-
-### Help
-A basic help menu
-```bash
-make help
-```
-
-### Install
-Building the binary
-```bash
-make install
-```
-
-### Start
-Starts the filesystem. Requires a running VPP instance using the default socket /run/vpp/stats.sock.
-
-May require a privileged user (sudo)
-```bash
-make start
-```
-
-### Stop
-Stops and unmounts the filesystem if it is not busy.
-
-May require a privileged user (sudo)
-```bash
-make stop
-```
-
-### Force unmount
-Forces the unmount of the filesystem even if it is busy.
-
-May require a privileged user (sudo)
-```bash
-make force-unmount
-```
-
-### Cleanup
-Cleaning stats_fs binary.
-
-May require a privileged user (sudo).
-```bash
-make clean
-```
-
-## Browsing the filesystem
-
-The default mountpoint is /run/vpp/stats_fs_dir.
-You can browse the filesystem as a regular user.
-Example:
-
-```bash
-cd /run/vpp/stats_fs_dir
-cd sys/node
-ls -al
-cat names
-```
-
-## Building and mounting the filesystem manually
-
-For more modularity, you can build and mount the filesystem manually.
-
-### Building
-Inside the local directory, you can build the go binary:
-```bash
-go build
-```
-
-### Mounting
-Then, ou can mount the filesystem with the local binary.
-
-May require a privileged user (sudo).
-
-The basic usage is:
-```bash
-./stats_fs <MOUNT_POINT>
-```
-
-**Options:**
- - debug \<true|false\> (default is false)
- - socket \<statSocket\> (default is /run/vpp/stats.sock) : VPP socket for stats
-
-
-### Unmounting the file system
-
-You can unmount the filesystem with the fusermount command.
-
-May require a privileged user (sudo)
-
-```bash
-fusermount -u /path/to/mountpoint
-```
-
-To force the unmount even if the resource is busy, add the -z option:
-```bash
-fusermount -uz /path/to/mountpoint
-```
diff --git a/extras/vpp_stats_fs/README.rst b/extras/vpp_stats_fs/README.rst
new file mode 100644
index 00000000000..d6635d146c0
--- /dev/null
+++ b/extras/vpp_stats_fs/README.rst
@@ -0,0 +1,148 @@
+.. _stats_fs_doc:
+
+VPP stats segment FUSE filesystem
+=================================
+
+The statfs binary allows to create a FUSE filesystem to expose and to
+browse the stats segment. It relies on the Go-FUSE library and requires
+Go-VPP stats bindings to work.
+
+The binary mounts a filesystem on the local machine with the data from
+the stats segments. The counters can be opened and read as files
+(e.g. in a Unix shell). Note that the value of a counter is determined
+when the corresponding file is opened (as for /proc/interrupts).
+
+Directories update their contents on epoch changes so that new counters
+get added to the filesystem.
+
+The script ``install.sh`` is responsible for building and installing
+the filesystem.
+
+Usage
+-----
+
+The local Makefile contains targets for all the possible interactions
+with the stats_f binary.
+
+Help
+~~~~
+
+A basic help menu
+
+.. code:: bash
+
+ make help
+
+Install
+~~~~~~~
+
+Building the binary
+
+.. code:: bash
+
+ make install
+
+Start
+~~~~~
+
+Starts the filesystem. Requires a running VPP instance using the default
+socket /run/vpp/stats.sock.
+
+May require a privileged user (sudo)
+
+.. code:: bash
+
+ make start
+
+Stop
+~~~~
+
+Stops and unmounts the filesystem if it is not busy.
+
+May require a privileged user (sudo)
+
+.. code:: bash
+
+ make stop
+
+Force unmount
+~~~~~~~~~~~~~
+
+Forces the unmount of the filesystem even if it is busy.
+
+May require a privileged user (sudo)
+
+.. code:: bash
+
+ make force-unmount
+
+Cleanup
+~~~~~~~
+
+Cleaning stats_fs binary.
+
+May require a privileged user (sudo).
+
+.. code:: bash
+
+ make clean
+
+Browsing the filesystem
+-----------------------
+
+The default mountpoint is /run/vpp/stats_fs_dir. You can browse the
+filesystem as a regular user. Example:
+
+.. code:: bash
+
+ cd /run/vpp/stats_fs_dir
+ cd sys/node
+ ls -al
+ cat names
+
+Building and mounting the filesystem manually
+---------------------------------------------
+
+For more modularity, you can build and mount the filesystem manually.
+
+Building
+~~~~~~~~
+
+Inside the local directory, you can build the go binary:
+
+.. code:: bash
+
+ go build
+
+Mounting
+~~~~~~~~
+
+Then, you can mount the filesystem with the local binary.
+
+May require a privileged user (sudo).
+
+The basic usage is:
+
+.. code:: bash
+
+ ./stats_fs <MOUNT_POINT>
+
+**Options:** - debug <true|false> (default is false) - socket
+<statSocket> (default is /run/vpp/stats.sock) : VPP socket for stats
+
+Unmounting the file system
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can unmount the filesystem with the fusermount command.
+
+May require a privileged user (sudo)
+
+.. code:: bash
+
+ fusermount -u /path/to/mountpoint
+
+To force the unmount even if the resource is busy, add the -z option:
+
+.. code:: bash
+
+ fusermount -uz /path/to/mountpoint
diff --git a/extras/vpptop/README.md b/extras/vpptop/README.md
deleted file mode 100644
index c0f4dc99b3a..00000000000
--- a/extras/vpptop/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# VPP Top Installation {#vpp_top_doc}
-
-[VPPTop]((https://github.com/PANTHEONtech/vpptop)) is a real-time data viewer for VPP interfaces and metrics displayed in dynamic terminal user interface, written in GO.
-
-Following make targets are available:
-
-**install** downloads and installs VPPTop including all external dependencies, binary API generator and latest version of GO. Running `make install-dep` (from the VPP top-level Makefile)
-is recommended.
-
-**cleanup** removes VPPTop repository from the target directory (/build-root/vpptop)
-
-**start** runs the VPPTop if installed
-
-**help** shows information about available commands
-
-The VPPTop is installed to be compatible with the given VPP version and may not work with other versions with different API. In that case, the VPPTop has to be re-installed.
-
-### GO variables management
-
-The installer depends on Golang environment variables GOROOT (for the GO installation) and GOPATH (for other binaries). Those variables are read from the environment and set to following values if not found:
-
-GOROOT=/root/.go/
-GOPATH=/root/go/
-
-If you have the GO already installed and have to run the installer with `sudo`, use the `-E` switch to provide those variables to the installer.
diff --git a/extras/vpptop/README.rst b/extras/vpptop/README.rst
new file mode 100644
index 00000000000..053659bca5f
--- /dev/null
+++ b/extras/vpptop/README.rst
@@ -0,0 +1,36 @@
+.. _vpp_top_doc:
+
+VPP Top Installation
+====================
+
+`VPPTop <(https://github.com/PANTHEONtech/vpptop)>`__ is a real-time
+data viewer for VPP interfaces and metrics displayed in dynamic terminal
+user interface, written in GO.
+
+Following make targets are available:
+
+* ``install`` downloads and installs VPPTop including all external dependencies, binary API generator
+ and latest version of GO. Running ``make install-dep`` (from the VPP top-level Makefile) is recommended.
+* ``cleanup`` removes VPPTop repository from the target directory (``/build-root/vpptop``)
+* ``start`` runs the VPPTop if installed
+* ``help`` shows information about available commands
+
+The VPPTop is installed to be compatible with the given VPP version and
+may not work with other versions with different API. In that case, the
+VPPTop has to be re-installed.
+
+GO variables management
+-----------------------
+
+The installer depends on Golang environment variables GOROOT (for the GO
+installation) and GOPATH (for other binaries). Those variables are read
+from the environment and set to following values if not found:
+
+::
+
+ GOROOT=/root/.go/ GOPATH=/root/go/
+
+
+If you have the GO already installed and have to run the installer with
+``sudo``, use the ``-E`` switch to provide those variables to the
+installer.