diff --git a/.gitignore b/.gitignore index ea3302415491..19378e8584aa 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ GTAGS /build-root/.doxygen-bootstrap.ok /build-root/.doxygen-siphon.dep /docs/_build +/docs/dynamic_includes /sphinx_venv !/docs/Makefile diff --git a/.gitreview b/.gitreview index 1db08df202d3..7be606e7e19b 100644 --- a/.gitreview +++ b/.gitreview @@ -2,3 +2,4 @@ host=gerrit.fd.io port=29418 project=vpp +defaultbranch=stable/2009 diff --git a/MAINTAINERS b/MAINTAINERS index e929020fe78c..7fdc59a4aa39 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -200,17 +200,17 @@ I: span M: N/A F: src/vnet/span -Crypto native Plugin +Plugin - Crypto - native I: crypto-native M: Damjan Marion F: src/plugins/crypto_native/ -Crypto openssl Plugin +Plugin - Crypto - OpenSSL I: crypto-openssl M: Damjan Marion F: src/plugins/crypto_openssl/ -Crypto ipsecmb Plugin +Plugin - Crypto - ipsecmb I: crypto-ipsecmb M: Neale Ranns F: src/plugins/crypto_ipsecmb/ diff --git a/Makefile b/Makefile index 29c4c9622546..53218e2bbca5 100644 --- a/Makefile +++ b/Makefile @@ -55,14 +55,14 @@ endif ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID)) PKG=deb -else ifeq ($(filter rhel centos fedora opensuse opensuse-leap opensuse-tumbleweed,$(OS_ID)),$(OS_ID)) +else ifeq ($(filter rhel centos fedora,$(OS_ID)),$(OS_ID)) PKG=rpm endif # +libganglia1-dev if building the gmond plugin DEB_DEPENDS = curl build-essential autoconf automake ccache -DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-systemd +DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-systemd dh-python DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config DEB_DEPENDS += lcov chrpath autoconf indent clang-format libnuma-dev DEB_DEPENDS += python3-all python3-setuptools check @@ -72,7 +72,7 @@ DEB_DEPENDS += python3-venv # ensurepip DEB_DEPENDS += python3-dev # needed for python3 -m pip install psutil # python3.6 on 16.04 requires python36-dev -LIBFFI=libffi6 # works on all but 20.04 +LIBFFI=libffi6 # works on all but 20.04 and debian-testing ifeq ($(OS_VERSION_ID),18.04) DEB_DEPENDS += python-dev python-all python-pip python-virtualenv @@ -83,16 +83,17 @@ else ifeq ($(OS_VERSION_ID),20.04) DEB_DEPENDS += libssl-dev DEB_DEPENDS += libelf-dev # for libbpf (af_xdp) LIBFFI=libffi7 -else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8) - DEB_DEPENDS += libssl-dev - DEB_DEPENDS += python-dev python-all python-pip python-virtualenv - APT_ARGS = -t jessie-backports else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-9) DEB_DEPENDS += libssl1.0-dev DEB_DEPENDS += python-all python-pip DEB_DEPENDS += python-dev python-all python-pip python-virtualenv +else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-10) + DEB_DEPENDS += libssl-dev + DEB_DEPENDS += libelf-dev # for libbpf (af_xdp) else DEB_DEPENDS += libssl-dev + DEB_DEPENDS += libelf-dev # for libbpf (af_xdp) + LIBFFI=libffi7 endif DEB_DEPENDS += $(LIBFFI) @@ -108,6 +109,7 @@ RPM_DEPENDS += libuuid-devel RPM_DEPENDS += mbedtls-devel RPM_DEPENDS += ccache RPM_DEPENDS += xmlto +RPM_DEPENDS += elfutils-libelf-devel ifeq ($(OS_ID),fedora) RPM_DEPENDS += dnf-utils @@ -120,7 +122,7 @@ ifeq ($(OS_ID),fedora) RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries' else ifeq ($(OS_ID)-$(OS_VERSION_ID),centos-8) RPM_DEPENDS += yum-utils - RPM_DEPENDS += compat-openssl10 + RPM_DEPENDS += compat-openssl10 openssl-devel RPM_DEPENDS += python2-devel python36-devel python3-ply RPM_DEPENDS += python3-virtualenv python3-jsonschema RPM_DEPENDS += cmake @@ -144,42 +146,6 @@ RPM_DEPENDS_DEBUG = glibc-debuginfo e2fsprogs-debuginfo RPM_DEPENDS_DEBUG += krb5-debuginfo openssl-debuginfo RPM_DEPENDS_DEBUG += zlib-debuginfo nss-softokn-debuginfo RPM_DEPENDS_DEBUG += yum-plugin-auto-update-debug-info -# lowercase- replace spaces with dashes. -SUSE_NAME= $(shell grep '^NAME=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g' | sed -e 's/ /-/' | awk '{print tolower($$0)}') -SUSE_ID= $(shell grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g' | cut -d' ' -f2) -RPM_SUSE_BUILDTOOLS_DEPS = autoconf automake ccache check-devel chrpath -RPM_SUSE_BUILDTOOLS_DEPS += clang cmake indent libtool make ninja python3-ply - -RPM_SUSE_DEVEL_DEPS = glibc-devel-static libnuma-devel -RPM_SUSE_DEVEL_DEPS += libopenssl-devel openssl-devel mbedtls-devel libuuid-devel - -RPM_SUSE_PYTHON_DEPS = python-devel python3-devel python-pip python3-pip -RPM_SUSE_PYTHON_DEPS += python-rpm-macros python3-rpm-macros - -RPM_SUSE_PLATFORM_DEPS = distribution-release shadow rpm-build - -ifeq ($(OS_ID),opensuse) -ifeq ($(SUSE_NAME),tumbleweed) - RPM_SUSE_DEVEL_DEPS = libboost_headers1_68_0-devel-1.68.0 libboost_thread1_68_0-devel-1.68.0 gcc - RPM_SUSE_PYTHON_DEPS += python3-ply python2-virtualenv -endif -ifeq ($(SUSE_ID),15.0) - RPM_SUSE_DEVEL_DEPS += libboost_headers-devel libboost_thread-devel gcc - RPM_SUSE_PYTHON_DEPS += python3-ply python2-virtualenv -else - RPM_SUSE_DEVEL_DEPS += libboost_headers1_68_0-devel-1.68.0 gcc6 - RPM_SUSE_PYTHON_DEPS += python-virtualenv -endif -endif - -ifeq ($(OS_ID),opensuse-leap) -ifeq ($(SUSE_ID),15.0) - RPM_SUSE_DEVEL_DEPS += libboost_headers-devel libboost_thread-devel gcc git curl - RPM_SUSE_PYTHON_DEPS += python3-ply python2-virtualenv -endif -endif - -RPM_SUSE_DEPENDS += $(RPM_SUSE_BUILDTOOLS_DEPS) $(RPM_SUSE_DEVEL_DEPS) $(RPM_SUSE_PYTHON_DEPS) $(RPM_SUSE_PLATFORM_DEPS) ifneq ($(wildcard $(STARTUP_DIR)/startup.conf),) STARTUP_CONF ?= $(STARTUP_DIR)/startup.conf @@ -310,13 +276,6 @@ bootstrap: .PHONY: install-dep install-dep: ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID)) -ifeq ($(OS_VERSION_ID),14.04) - @sudo -E apt-get $(CONFIRM) $(FORCE) install software-properties-common -endif -ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8) - @grep -q jessie-backports /etc/apt/sources.list /etc/apt/sources.list.d/* 2> /dev/null \ - || ( echo "Please install jessie-backports" ; exit 1 ) -endif @sudo -E apt-get update @sudo -E apt-get $(APT_ARGS) $(CONFIRM) $(FORCE) install $(DEB_DEPENDS) else ifneq ("$(wildcard /etc/redhat-release)","") @@ -326,8 +285,9 @@ ifeq ($(OS_ID),rhel) @sudo -E yum install $(CONFIRM) $(RPM_DEPENDS) @sudo -E debuginfo-install $(CONFIRM) glibc openssl-libs mbedtls-devel zlib else ifeq ($(OS_ID)-$(OS_VERSION_ID),centos-8) - @sudo -E dnf install $(CONFIRM) epel-release - @sudo -E dnf config-manager --set-enabled PowerTools + @sudo -E dnf install $(CONFIRM) dnf-plugins-core epel-release + @sudo -E dnf config-manager --set-enabled \ + $(shell dnf repolist all 2>/dev/null|grep -i powertools|cut -d' ' -f1) @sudo -E dnf groupinstall $(CONFIRM) $(RPM_DEPENDS_GROUPS) @sudo -E dnf install $(CONFIRM) $(RPM_DEPENDS) else ifeq ($(OS_ID),centos) @@ -340,17 +300,8 @@ else ifeq ($(OS_ID),fedora) @sudo -E dnf install $(CONFIRM) $(RPM_DEPENDS) @sudo -E debuginfo-install $(CONFIRM) glibc openssl-libs mbedtls-devel zlib endif -else ifeq ($(filter opensuse-tumbleweed,$(OS_ID)),$(OS_ID)) - @sudo -E zypper refresh - @sudo -E zypper install -y $(RPM_SUSE_DEPENDS) -else ifeq ($(filter opensuse-leap,$(OS_ID)),$(OS_ID)) - @sudo -E zypper refresh - @sudo -E zypper install -y $(RPM_SUSE_DEPENDS) -else ifeq ($(filter opensuse,$(OS_ID)),$(OS_ID)) - @sudo -E zypper refresh - @sudo -E zypper install -y $(RPM_SUSE_DEPENDS) else - $(error "This option currently works only on Ubuntu, Debian, RHEL, CentOS or openSUSE systems") + $(error "This option currently works only on Ubuntu, Debian, RHEL, or CentOS systems") endif git config commit.template .git_commit_template.txt diff --git a/RELEASE.md b/RELEASE.md index 45b3f1ad08e8..dd9d48bf1ff0 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -32,7 +32,661 @@ @page release_notes_2009 Release notes for VPP 20.09 -TBD +More than 458 commits since the previous release, including 266 fixes. + +## Release Highlights + +The FD.io VPP 20.09 release added a number of notable new features. In plugins, +the I/O layer added support for the Linux AF\_XDP interface with the AF\_XDP +plugin. New plugins where added supporting both the Wireguard security protocol +and CNAT destination based address translation, and the existing IKEv2 plugin +added support for NAT-T. In the cryptography layer, support was added for +synchronous software crypto engines, enabling users to allocate dedicated crypto +worker threads. The flow layer added support for steering IPSEC ESP/AH flows to +worker threads. GRO support was added to the packet coalescing library. + +This release introduces the new FD.io VPP API change policy to ensure +backwards-compatibility. The policy will ensure seamless upgrades to new +versions of FD.io VPP in future, provided no "in-progress" or deprecated APIs +are in use. Enabling the FD.io community to enjoy the benefits of new releases, +while minimizing the work involved in staying current. + +If you dive into the implementation, you will note that policy in action. A +number of modified API messages have had their original versions maintained to +ensure compatibility. + +Reflecting the new policy we added two new sections to the release notes +describing: +- Newly deprecated API messages: please note that if you are using a deprecated +message, they will soon be removed in a subsequent release. Collaborate with +the feature maintainer on the best approach to mitigate. +- In-progress API messages: They are work-in-progress, and are *not* subject to +the policy, and may change or even be removed at any time. Please collaborate +with the feature maintainer on plans to productize the message before using in +any product. In-progress APIs must eventually become stable or be removed. + +## Features + +- VNET + - Crypto Infra + - Add chacha20-poly1305 algo (61f49aa38) + - Asynchronous crypto engines (2284817ea) + - Add asynchronous crypto APIs (0c936b147) + - Added support for optimized cryptodev API (ef80ad6bf) + - FLOW + - Added ability to steer IPSec ESP/AH flows to worker threads (d4c3666b9) + - Added the vnet/flow API (d0236f725) + - GENEVE + - Support geneve interface acting as a bvi (7fc88cf3a) + - GSO + - Added software GRO support (f382b06fe) + - IPSec + - Dedicated IPSec interface type (dd4ccf262) + - Deprecate old interface API (e6df80de4) + - Interface Common + - Support configuring RSS steering queues (c4665093c) + - Native Virtio Drivers + - Add vhost sw\_if\_index filter for sw\_interface\_vhost\_user\_dump (a0e8d9669) + - Add modern device support (379aac395) + - Add virtio 1.1 api flags (518251bc8) + - TAP Drivers + - Add gro support (9e2a78564) + - Add virtio 1.1 API flag (50bd16559) + - TCP + - Track reorder with selective acknowledgments (cc4d6d022) +- Plugins + - AF\_XDP driver + - New plugin for Linux AF\_XDP input (4a76d6f6d) + - CNat + - New plugin for destination based NAT (29f3c7d2e) + - Wireguard + - New plugin, initial implementation of wireguard protocol (edca1325c) + - Crypto - OpenSSL + - Add chacha20-poly1305 support to crypto-openssl (1b6ed022e) + - DPDK + - Device\_id sorted order for cryptodev (5a849e3b3) + - Call the meson-based build instead of Makefiles (73903d7e8) + - Internet Key Exchange (IKEv2) Protocol + - Add support for NAT traversal (NAT-T) (4362baa33) + - Add profile dump API (6a9bd8188) + - Add support for AES-GCM cipher in IKE (a7b963df2) + - Add SA dump API (a340fe1ac) + - Network Delay Simulator + - Basic reorder support (e6c3e8f0e) +- VPP Comms Library + - Nest vcl\_mq\_epfd to support epoll\_wait without high CPU usage (4266d4d5f) + - Support connected udp listens (1e96617d9) + - Support inter worker rpc (40c07ce7a) + - Support multi-threads with session migration (a3a489691) +- Vector Library + - Add recursive macro expander to debug cli (961e3c842) +- Binary API Libraries + - Add new stream message convention (f5db3711b) + - Make VPP api handlers endian independent (e796a1873) +- Infrastructure Library + - Multiarch support for OCTEONTX2 SoC (e2f5236dc) + +## Known issues + +For the full list of issues please refer to fd.io [JIRA](https://jira.fd.io). + +## Fixed issues + +For the full list of fixed issues please refer to: +- fd.io [JIRA](https://jira.fd.io) +- git [commit log](https://git.fd.io/vpp/log/?h=stable/2009) + + +## API changes + +Description of results: + +* _Definition changed_: indicates that the API file was modified between releases. +* _Only in image_: indicates the API is new for this release. +* _Only in file_: indicates the API has been removed in this release. + +Message Name | Result +-------------------------------------------------------------|------------------ +adl_allowlist_enable_disable | only in image +adl_allowlist_enable_disable_reply | only in image +adl_interface_enable_disable | only in image +adl_interface_enable_disable_reply | only in image +bond_add_member | only in image +bond_add_member_reply | only in image +bond_create2 | only in image +bond_create2_reply | only in image +bond_detach_member | only in image +bond_detach_member_reply | only in image +cnat_add_del_snat_prefix | only in image +cnat_add_del_snat_prefix_reply | only in image +cnat_session_details | only in image +cnat_session_dump | only in image +cnat_session_purge | only in image +cnat_session_purge_reply | only in image +cnat_set_snat_addresses | only in image +cnat_set_snat_addresses_reply | only in image +cnat_translation_del | only in image +cnat_translation_del_reply | only in image +cnat_translation_details | only in image +cnat_translation_dump | only in image +cnat_translation_update | only in image +cnat_translation_update_reply | only in image +crypto_set_async_dispatch | only in image +crypto_set_async_dispatch_reply | only in image +crypto_set_handler | only in image +crypto_set_handler_reply | only in image +crypto_sw_scheduler_set_worker | only in image +crypto_sw_scheduler_set_worker_reply | only in image +det44_add_del_map | only in image +det44_add_del_map_reply | only in image +det44_close_session_in | only in image +det44_close_session_in_reply | only in image +det44_close_session_out | only in image +det44_close_session_out_reply | only in image +det44_forward | only in image +det44_forward_reply | only in image +det44_get_timeouts | only in image +det44_get_timeouts_reply | only in image +det44_interface_add_del_feature | only in image +det44_interface_add_del_feature_reply | only in image +det44_interface_details | only in image +det44_interface_dump | only in image +det44_map_details | only in image +det44_map_dump | only in image +det44_plugin_enable_disable | only in image +det44_plugin_enable_disable_reply | only in image +det44_reverse | only in image +det44_reverse_reply | only in image +det44_session_details | only in image +det44_session_dump | only in image +det44_set_timeouts | only in image +det44_set_timeouts_reply | only in image +flow_add | only in image +flow_add_reply | only in image +flow_del | only in image +flow_del_reply | only in image +flow_disable | only in image +flow_disable_reply | only in image +flow_enable | only in image +flow_enable_reply | only in image +geneve_add_del_tunnel2 | only in image +geneve_add_del_tunnel2_reply | only in image +gtpu_add_del_tunnel | definition changed +gtpu_tunnel_details | definition changed +gtpu_tunnel_update_tteid | only in image +gtpu_tunnel_update_tteid_reply | only in image +ikev2_child_sa_details | only in image +ikev2_child_sa_dump | only in image +ikev2_nonce_get | only in image +ikev2_nonce_get_reply | only in image +ikev2_profile_details | only in image +ikev2_profile_dump | only in image +ikev2_profile_set_ts | definition changed +ikev2_sa_details | only in image +ikev2_sa_dump | only in image +ikev2_set_esp_transforms | definition changed +ikev2_set_ike_transforms | definition changed +ikev2_set_responder | definition changed +ikev2_traffic_selector_details | only in image +ikev2_traffic_selector_dump | only in image +ipsec_itf_create | only in image +ipsec_itf_create_reply | only in image +ipsec_itf_delete | only in image +ipsec_itf_delete_reply | only in image +ipsec_itf_details | only in image +ipsec_itf_dump | only in image +ipsec_set_async_mode | only in image +ipsec_set_async_mode_reply | only in image +map_domains_get | only in image +map_domains_get_reply | only in image +nat44_add_del_static_mapping_v2 | only in image +nat44_add_del_static_mapping_v2_reply | only in image +nat_show_config_2 | only in image +nat_show_config_2_reply | only in image +nsim_configure2 | only in image +nsim_configure2_reply | only in image +pg_interface_enable_disable_coalesce | only in image +pg_interface_enable_disable_coalesce_reply | only in image +sr_policies_with_sl_index_details | only in image +sr_policies_with_sl_index_dump | only in image +sw_bond_interface_details | only in image +sw_bond_interface_dump | only in image +sw_member_interface_details | only in image +sw_member_interface_dump | only in image +trace_details | only in image +trace_dump | only in image +trace_dump_reply | only in image +virtio_pci_create_v2 | only in image +virtio_pci_create_v2_reply | only in image +wireguard_interface_create | only in image +wireguard_interface_create_reply | only in image +wireguard_interface_delete | only in image +wireguard_interface_delete_reply | only in image +wireguard_interface_details | only in image +wireguard_interface_dump | only in image +wireguard_peer_add | only in image +wireguard_peer_add_reply | only in image +wireguard_peer_remove | only in image +wireguard_peer_remove_reply | only in image +wireguard_peers_details | only in image +wireguard_peers_dump | only in image + +Found 123 api message signature differences + + +### Newly deprecated API messages + +These messages are still there in the API, but can and probably +will disappear in the next release. + +- bond_create +- bond_detach_slave +- bond_detach_slave_reply +- bond_enslave +- cop_interface_enable_disable +- cop_interface_enable_disable_reply +- cop_whitelist_enable_disable +- cop_whitelist_enable_disable_reply +- geneve_add_del_tunnel +- ipsec_tunnel_if_add_del +- ipsec_tunnel_if_set_sa +- ipsec_tunnel_if_set_sa_reply +- map_domain_dump +- nat_det_add_del_map +- nat_det_add_del_map_reply +- nat_det_close_session_in +- nat_det_close_session_in_reply +- nat_det_close_session_out +- nat_det_close_session_out_reply +- nat_det_forward +- nat_det_forward_reply +- nat_det_map_details +- nat_det_map_dump +- nat_det_reverse +- nat_det_reverse_reply +- nat_det_session_details +- nat_det_session_dump +- nat_show_config +- nsim_configure +- nsim_configure_reply +- sw_interface_bond_dump +- sw_interface_slave_dump +- virtio_pci_create +- virtio_pci_create_reply + +### In-progress API messages + +These messages are provided for testing and experimentation only. +They are *not* subject to any compatibility process, +and therefore can arbitrarily change or disappear at *any* moment. +Also they may have less than satisfactory testing, making +them unsuitable for other use than the technology preview. +If you are intending to use these messages in production projects, +please collaborate with the feature maintainer on their productization. + +- abf_itf_attach_add_del +- abf_itf_attach_add_del_reply +- abf_itf_attach_details +- abf_itf_attach_dump +- abf_plugin_get_version +- abf_plugin_get_version_reply +- abf_policy_add_del +- abf_policy_add_del_reply +- abf_policy_details +- abf_policy_dump +- adl_allowlist_enable_disable +- adl_allowlist_enable_disable_reply +- adl_interface_enable_disable +- adl_interface_enable_disable_reply +- af_xdp_create +- af_xdp_create_reply +- af_xdp_delete +- af_xdp_delete_reply +- cnat_add_del_snat_prefix +- cnat_add_del_snat_prefix_reply +- cnat_session_details +- cnat_session_dump +- cnat_session_purge +- cnat_session_purge_reply +- cnat_set_snat_addresses +- cnat_set_snat_addresses_reply +- cnat_translation_del +- cnat_translation_del_reply +- cnat_translation_details +- cnat_translation_dump +- cnat_translation_update +- cnat_translation_update_reply +- crypto_sw_scheduler_set_worker +- crypto_sw_scheduler_set_worker_reply +- det44_get_timeouts_reply +- det44_interface_add_del_feature +- det44_interface_add_del_feature_reply +- det44_interface_details +- det44_interface_dump +- det44_plugin_enable_disable +- det44_plugin_enable_disable_reply +- det44_set_timeouts +- det44_set_timeouts_reply +- flow_add +- flow_add_reply +- flow_del +- flow_del_reply +- flow_disable +- flow_disable_reply +- flow_enable +- flow_enable_reply +- gbp_bridge_domain_add +- gbp_bridge_domain_add_reply +- gbp_bridge_domain_del +- gbp_bridge_domain_del_reply +- gbp_bridge_domain_details +- gbp_bridge_domain_dump +- gbp_bridge_domain_dump_reply +- gbp_contract_add_del +- gbp_contract_add_del_reply +- gbp_contract_details +- gbp_contract_dump +- gbp_endpoint_add +- gbp_endpoint_add_reply +- gbp_endpoint_del +- gbp_endpoint_del_reply +- gbp_endpoint_details +- gbp_endpoint_dump +- gbp_endpoint_group_add +- gbp_endpoint_group_add_reply +- gbp_endpoint_group_del +- gbp_endpoint_group_del_reply +- gbp_endpoint_group_details +- gbp_endpoint_group_dump +- gbp_ext_itf_add_del +- gbp_ext_itf_add_del_reply +- gbp_ext_itf_details +- gbp_ext_itf_dump +- gbp_recirc_add_del +- gbp_recirc_add_del_reply +- gbp_recirc_details +- gbp_recirc_dump +- gbp_route_domain_add +- gbp_route_domain_add_reply +- gbp_route_domain_del +- gbp_route_domain_del_reply +- gbp_route_domain_details +- gbp_route_domain_dump +- gbp_route_domain_dump_reply +- gbp_subnet_add_del +- gbp_subnet_add_del_reply +- gbp_subnet_details +- gbp_subnet_dump +- gbp_vxlan_tunnel_add +- gbp_vxlan_tunnel_add_reply +- gbp_vxlan_tunnel_del +- gbp_vxlan_tunnel_del_reply +- gbp_vxlan_tunnel_details +- gbp_vxlan_tunnel_dump +- ikev2_child_sa_details +- ikev2_child_sa_dump +- ikev2_initiate_del_child_sa +- ikev2_initiate_del_child_sa_reply +- ikev2_initiate_del_ike_sa +- ikev2_initiate_del_ike_sa_reply +- ikev2_initiate_rekey_child_sa +- ikev2_initiate_rekey_child_sa_reply +- ikev2_initiate_sa_init +- ikev2_initiate_sa_init_reply +- ikev2_nonce_get +- ikev2_nonce_get_reply +- ikev2_profile_add_del +- ikev2_profile_add_del_reply +- ikev2_profile_details +- ikev2_profile_dump +- ikev2_profile_set_auth +- ikev2_profile_set_auth_reply +- ikev2_profile_set_id +- ikev2_profile_set_id_reply +- ikev2_profile_set_ipsec_udp_port +- ikev2_profile_set_ipsec_udp_port_reply +- ikev2_profile_set_liveness +- ikev2_profile_set_liveness_reply +- ikev2_profile_set_ts +- ikev2_profile_set_ts_reply +- ikev2_profile_set_udp_encap +- ikev2_profile_set_udp_encap_reply +- ikev2_sa_details +- ikev2_sa_dump +- ikev2_set_esp_transforms +- ikev2_set_esp_transforms_reply +- ikev2_set_ike_transforms +- ikev2_set_ike_transforms_reply +- ikev2_set_local_key +- ikev2_set_local_key_reply +- ikev2_set_responder +- ikev2_set_responder_reply +- ikev2_set_sa_lifetime +- ikev2_set_sa_lifetime_reply +- ikev2_set_tunnel_interface +- ikev2_set_tunnel_interface_reply +- ikev2_traffic_selector_details +- ikev2_traffic_selector_dump +- l2_emulation +- l2_emulation_reply +- mdata_enable_disable +- mdata_enable_disable_reply +- nat44_add_del_static_mapping_v2 +- nat44_add_del_static_mapping_v2_reply +- oddbuf_enable_disable +- oddbuf_enable_disable_reply +- pg_interface_enable_disable_coalesce +- pg_interface_enable_disable_coalesce_reply +- sample_macswap_enable_disable +- sample_macswap_enable_disable_reply +- sr_policies_with_sl_index_details +- sr_policies_with_sl_index_dump +- sw_interface_set_vxlan_gbp_bypass +- sw_interface_set_vxlan_gbp_bypass_reply +- trace_details +- trace_dump +- trace_dump_reply +- vxlan_gbp_tunnel_add_del +- vxlan_gbp_tunnel_add_del_reply +- vxlan_gbp_tunnel_details +- vxlan_gbp_tunnel_dump +- wireguard_interface_create +- wireguard_interface_create_reply +- wireguard_interface_delete +- wireguard_interface_delete_reply +- wireguard_interface_details +- wireguard_interface_dump +- wireguard_peer_add +- wireguard_peer_add_reply +- wireguard_peer_remove +- wireguard_peer_remove_reply +- wireguard_peers_details +- wireguard_peers_dump + +### Patches that changed API definitions + +| @c src/vpp/api/vpe.api || +| ------- | ------- | +| [d0236f725](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=d0236f725) | flow: add vnet/flow formal API | + +| @c src/vnet/crypto/crypto.api || +| ------- | ------- | +| [4035daffd](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4035daffd) | crypto: Crypto set handler API to support set all as CLI | +| [0c936b147](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=0c936b147) | crypto: Add async crypto APIs | + +| @c src/vnet/cop/cop.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [ac0326fc5](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ac0326fc5) | adl: move allow/deny list function to plugin | + +| @c src/vnet/lisp-gpe/lisp_gpe.api || +| ------- | ------- | +| [4ab5190eb](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4ab5190eb) | lisp: API cleanup | + +| @c src/vnet/vxlan-gbp/vxlan_gbp.api || +| ------- | ------- | +| [f72b1aff7](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=f72b1aff7) | vxlan-gbp: Mark APIs as in-progress | + +| @c src/vnet/flow/flow_types.api || +| ------- | ------- | +| [34bfa50b6](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=34bfa50b6) | flow: code refactor | +| [d0236f725](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=d0236f725) | flow: add vnet/flow formal API | + +| @c src/vnet/flow/flow.api || +| ------- | ------- | +| [d0236f725](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=d0236f725) | flow: add vnet/flow formal API | + +| @c src/vnet/srv6/sr.api || +| ------- | ------- | +| [30fa97dc6](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=30fa97dc6) | sr: new messages created to return sl index for segment lists in a sr policy | + +| @c src/vnet/pg/pg.api || +| ------- | ------- | +| [f382b06fe](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=f382b06fe) | gso: packet coalesce library | +| [0cf528233](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=0cf528233) | gso: fix the udp checksum in test | + +| @c src/vnet/geneve/geneve.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [7fc88cf3a](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=7fc88cf3a) | geneve: support geneve interface acting as a bvi | + +| @c src/vnet/lisp-cp/one.api || +| ------- | ------- | +| [4ab5190eb](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4ab5190eb) | lisp: API cleanup | + +| @c src/vnet/lisp-cp/lisp.api || +| ------- | ------- | +| [4ab5190eb](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4ab5190eb) | lisp: API cleanup | + +| @c src/vnet/devices/tap/tapv2.api || +| ------- | ------- | +| [50bd16559](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=50bd16559) | tap: add virtio 1.1 API flag | + +| @c src/vnet/devices/virtio/vhost_user.api || +| ------- | ------- | +| [a0e8d9669](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=a0e8d9669) | virtio: add vhost sw_if_index filter for sw_interface_vhost_user_dump | + +| @c src/vnet/devices/virtio/virtio.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [518251bc8](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=518251bc8) | virtio: add virtio 1.1 api flags | + +| @c src/vnet/ipsec/ipsec.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [2e84d6655](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=2e84d6655) | ipsec: add ipsec set async mode api | +| [e6df80de4](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=e6df80de4) | ipsec: Deprecate old interface API | +| [dd4ccf262](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=dd4ccf262) | ipsec: Dedicated IPSec interface type | + +| @c src/vnet/bonding/bond.api || +| ------- | ------- | +| [ea7178631](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ea7178631) | bonding: add bond_create2 API to include gso option | +| [4c4223edf](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4c4223edf) | bonding lacp: replace slave string with member | + +| @c src/vnet/ip/ip_types.api || +| ------- | ------- | +| [d0236f725](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=d0236f725) | flow: add vnet/flow formal API | + +| @c src/plugins/wireguard/wireguard.api || +| ------- | ------- | +| [edca1325c](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=edca1325c) | wireguard: initial implementation of wireguard protocol | + +| @c src/plugins/map/map.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [ac0326fc5](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ac0326fc5) | adl: move allow/deny list function to plugin | +| [f5db3711b](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=f5db3711b) | api: add new stream message convention | + +| @c src/plugins/lacp/lacp.api || +| ------- | ------- | +| [4c4223edf](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4c4223edf) | bonding lacp: replace slave string with member | + +| @c src/plugins/l2e/l2e.api || +| ------- | ------- | +| [f733e7ade](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=f733e7ade) | l2e: mark API as in-progress | + +| @c src/plugins/ikev2/ikev2.api || +| ------- | ------- | +| [a340fe1ac](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=a340fe1ac) | ikev2: add SA dump API | +| [459d17bb7](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=459d17bb7) | ikev2: refactor and test profile dump API | +| [ac46e3b1d](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ac46e3b1d) | ikev2: API downgrade due to lack of ikev2 tests | +| [6a9bd8188](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=6a9bd8188) | ikev2: add profile dump API | + +| @c src/plugins/ikev2/ikev2_types.api || +| ------- | ------- | +| [a340fe1ac](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=a340fe1ac) | ikev2: add SA dump API | +| [459d17bb7](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=459d17bb7) | ikev2: refactor and test profile dump API | +| [6a9bd8188](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=6a9bd8188) | ikev2: add profile dump API | + +| @c src/plugins/tracedump/tracedump.api || +| ------- | ------- | +| [65b65a469](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=65b65a469) | misc: add tracedump API plugin | + +| @c src/plugins/gtpu/gtpu.api || +| ------- | ------- | +| [9ebbb5c41](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=9ebbb5c41) | gtpu: support separate rx-decap and encap-tx teid values | + +| @c src/plugins/gbp/gbp.api || +| ------- | ------- | +| [d2f8fb9c7](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=d2f8fb9c7) | gbp: mark APIs as in-progress | + +| @c src/plugins/acl/acl.api || +| ------- | ------- | +| [24ee40a5c](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=24ee40a5c) | acl: correct acl vat help message | + +| @c src/plugins/nat/dslite/dslite.api || +| ------- | ------- | +| [603e75465](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=603e75465) | nat: move deterministic nat to det44 sub feature | + +| @c src/plugins/nat/det44/det44.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [603e75465](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=603e75465) | nat: move deterministic nat to det44 sub feature | + +| @c src/plugins/nat/nat_types.api || +| ------- | ------- | +| [96068d6b9](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=96068d6b9) | nat: nat66 to plugin | + +| @c src/plugins/nat/nat.api || +| ------- | ------- | +| [6484f4b9c](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=6484f4b9c) | nat: twice-nat static mapping pool address | +| [edc816355](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=edc816355) | nat: fix type in api message | +| [603e75465](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=603e75465) | nat: move deterministic nat to det44 sub feature | +| [96068d6b9](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=96068d6b9) | nat: nat66 to plugin | + +| @c src/plugins/nat/nat66/nat66.api || +| ------- | ------- | +| [96068d6b9](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=96068d6b9) | nat: nat66 to plugin | + +| @c src/plugins/cnat/cnat.api || +| ------- | ------- | +| [29f3c7d2e](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=29f3c7d2e) | cnat: Destination based NAT | + +| @c src/plugins/abf/abf.api || +| ------- | ------- | +| [df494dafa](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=df494dafa) | abf: mark API as in-progress | + +| @c src/plugins/adl/adl.api || +| ------- | ------- | +| [ac0326fc5](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=ac0326fc5) | adl: move allow/deny list function to plugin | + +| @c src/plugins/nsim/nsim.api || +| ------- | ------- | +| [00f21fb2f](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=00f21fb2f) | api: clean up use of deprecated flag | +| [e6c3e8f0e](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=e6c3e8f0e) | nsim: basic reorder support | + +| @c src/plugins/crypto_sw_scheduler/crypto_sw_scheduler.api || +| ------- | ------- | +| [0c936b147](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=0c936b147) | crypto: Add async crypto APIs | + +| @c src/plugins/dhcp/dhcp.api || +| ------- | ------- | +| [bad679291](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=bad679291) | api: register endian handlers for reply messages | + +| @c src/plugins/af_xdp/af_xdp.api || +| ------- | ------- | +| [4a76d6f6d](https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4a76d6f6d) | af_xdp: AF_XDP input plugin | + @page release_notes_20051 Release notes for VPP 20.05.1 diff --git a/build/external/packages.mk b/build/external/packages.mk index 005c2a958b2e..bcd1d38a3870 100644 --- a/build/external/packages.mk +++ b/build/external/packages.mk @@ -79,7 +79,7 @@ $(B)/.$1.patch.ok: $(B)/.$1.extract.ok ifneq ($$(wildcard $$($1_patch_dir)/*.patch),) @for f in $$($1_patch_dir)/*.patch ; do \ echo "Applying patch: $$$$(basename $$$$f)" ; \ - patch -p1 -d $$($1_src_dir) < $$$$f ; \ + patch -p1 -d $$($1_src_dir) --no-backup-if-mismatch < $$$$f ; \ done endif @touch $$@ diff --git a/build/external/packages/libbpf.mk b/build/external/packages/libbpf.mk index 90ff54b433c1..aa72832afe15 100644 --- a/build/external/packages/libbpf.mk +++ b/build/external/packages/libbpf.mk @@ -27,8 +27,10 @@ else LIBBPF_CFLAGS+= -O2 endif -IF_XDP:=$(shell echo "\#include " | $(CC) -E -xc - > /dev/null 2>&1) -IF_XDP:=$(.SHELLSTATUS) +# check for libelf, zlib and kernel if_xdp.h presence +LIBBPF_DEPS_CHECK:="\#include \\n\#include \\n\#include \\nint main(void){return 0;}" +LIBBPF_DEPS_CHECK:=$(shell echo -e $(LIBBPF_DEPS_CHECK) | $(CC) -xc -lelf -lz -o /dev/null - > /dev/null 2>&1) +LIBBPF_DEPS_CHECK:=$(.SHELLSTATUS) define libbpf_config_cmds @true @@ -46,8 +48,8 @@ define libbpf_install_cmds $(call libbpf_build_cmds__,install,$(libbpf_install_log)) endef -ifneq ($(IF_XDP),0) - $(warning "linux/if_xdp.h was not found on this system. libbpf will be skipped.") +ifneq ($(LIBBPF_DEPS_CHECK),0) + $(warning "Missing libbpf dependencies. libbpf will be skipped.") libbpf-install: @true else diff --git a/build/external/patches/README b/build/external/patches/README index 0bead2ef6292..b2c66d9bdc51 100644 --- a/build/external/patches/README +++ b/build/external/patches/README @@ -24,7 +24,7 @@ for release tag “v2.2.0” and will create a branch named “two_dot_two”. 5. Create the patch files with format-patch. This creates all the patch files for your branch (two_dot_two), with your latest commits as the last ones. - # git format-patch master..two_dot_two + # git format-patch main..two_dot_two 6. Copy, add and commit the new patches into the patches directory. diff --git a/build/external/patches/dpdk_20.08/0001-net-iavf-deprecate-i40evf-pmd.patch b/build/external/patches/dpdk_20.08/0001-net-iavf-deprecate-i40evf-pmd.patch new file mode 100644 index 000000000000..aa660e701320 --- /dev/null +++ b/build/external/patches/dpdk_20.08/0001-net-iavf-deprecate-i40evf-pmd.patch @@ -0,0 +1,232 @@ +From bd048f56bc4b85fed31f34db676f1ad67c86bd16 Mon Sep 17 00:00:00 2001 +From: Robin Zhang +Date: Mon, 19 Apr 2021 03:05:39 +0000 +Subject: [PATCH] net/iavf: deprecate i40evf pmd + +The i40evf PMD will be deprecated, iavf will be the only VF driver for +Intel 700 serial (i40e) NIC family. To reach this, there will be 2 steps: + +Step 1: iavf will be the default VF driver, while i40evf still can be +selected by devarg: "driver=i40evf". +This is covered by this patch, which include: +1) add all 700 serial NIC VF device ID into iavf PMD +2) skip probe if devargs contain "driver=i40evf" in iavf +3) continue probe if devargs contain "driver=i40evf" in i40evf + +Step 2: i40evf and related devarg are removed, this will happen at DPDK +21.11 + +Between step 1 and step 2, no new feature will be added into i40evf except +bug fix. + +Signed-off-by: Robin Zhang +Acked-by: Qi Zhang +Acked-by: Ferruh Yigit +Acked-by: Beilei Xing +--- + doc/guides/nics/intel_vf.rst | 6 +++ + doc/guides/rel_notes/deprecation.rst | 8 ++++ + drivers/common/iavf/iavf_devids.h | 2 + + drivers/net/i40e/i40e_ethdev_vf.c | 45 ++++++++++++++++++++++ + drivers/net/iavf/iavf_ethdev.c | 57 +++++++++++++++++++++++++++- + 5 files changed, 116 insertions(+), 2 deletions(-) + +diff --git a/doc/guides/nics/intel_vf.rst b/doc/guides/nics/intel_vf.rst +index ade5152595..b95200698d 100644 +--- a/doc/guides/nics/intel_vf.rst ++++ b/doc/guides/nics/intel_vf.rst +@@ -88,6 +88,12 @@ For more detail on SR-IOV, please refer to the following documents: + assignment in hypervisor. Take qemu for example, the device assignment should carry the IAVF device id (0x1889) like + ``-device vfio-pci,x-pci-device-id=0x1889,host=03:0a.0``. + ++ Starting from DPDK 21.05, the default VF driver for Intel® 700 Series Ethernet Controller will be IAVF. No new feature ++ will be added into i40evf except bug fix until it's removed in DPDK 21.11. Between DPDK 21.05 and 21.11, by using the ++ ``devargs`` option ``driver=i40evf``, i40evf PMD still can be used on Intel® 700 Series Ethernet Controller, for example:: ++ ++ -a 81:02.0,driver=i40evf ++ + The PCIE host-interface of Intel Ethernet Switch FM10000 Series VF infrastructure + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst +index c2770feeae..25caf4e52d 100644 +--- a/doc/guides/rel_notes/deprecation.rst ++++ b/doc/guides/rel_notes/deprecation.rst +@@ -335,3 +335,11 @@ Deprecation Notices + ``make``. Given environments are too much variables for such a simple script, + it will be removed in DPDK 20.11. + Some useful parts may be converted into specific scripts. ++ ++* i40e: As there are both i40evf and iavf pmd, the functions of them are ++ duplicated. And now more and more advanced features are developed on iavf. ++ To keep consistent with kernel driver's name ++ (https://patchwork.ozlabs.org/patch/970154/), i40evf is no need to maintain. ++ Starting from 21.05, the default VF driver of i40e will be iavf, but i40evf ++ can still be used if users specify the devarg "driver=i40evf". I40evf will ++ be deleted in DPDK 21.11. +diff --git a/drivers/common/iavf/iavf_devids.h b/drivers/common/iavf/iavf_devids.h +index 2e63aac289..1c3acb586d 100644 +--- a/drivers/common/iavf/iavf_devids.h ++++ b/drivers/common/iavf/iavf_devids.h +@@ -13,5 +13,7 @@ + #define IAVF_DEV_ID_VF_HV 0x1571 + #define IAVF_DEV_ID_ADAPTIVE_VF 0x1889 + #define IAVF_DEV_ID_X722_VF 0x37CD ++#define IAVF_DEV_ID_X722_A0_VF 0x374D ++ + + #endif /* _IAVF_DEVIDS_H_ */ +diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c +index 69cab8e739..3d61c092d8 100644 +--- a/drivers/net/i40e/i40e_ethdev_vf.c ++++ b/drivers/net/i40e/i40e_ethdev_vf.c +@@ -1592,9 +1592,53 @@ i40evf_dev_uninit(struct rte_eth_dev *eth_dev) + return 0; + } + ++static int ++i40evf_check_driver_handler(__rte_unused const char *key, ++ const char *value, __rte_unused void *opaque) ++{ ++ if (strcmp(value, "i40evf")) ++ return -1; ++ ++ return 0; ++} ++ ++static int ++i40evf_driver_selected(struct rte_devargs *devargs) ++{ ++ struct rte_kvargs *kvlist; ++ const char *key = "driver"; ++ int ret = 0; ++ ++ if (devargs == NULL) ++ return 0; ++ ++ kvlist = rte_kvargs_parse(devargs->args, NULL); ++ if (kvlist == NULL) ++ return 0; ++ ++ if (!rte_kvargs_count(kvlist, key)) ++ goto exit; ++ ++ /* i40evf driver selected when there's a key-value pair: ++ * driver=i40evf ++ */ ++ if (rte_kvargs_process(kvlist, key, ++ i40evf_check_driver_handler, NULL) < 0) ++ goto exit; ++ ++ ret = 1; ++ ++exit: ++ rte_kvargs_free(kvlist); ++ return ret; ++} ++ + static int eth_i40evf_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, + struct rte_pci_device *pci_dev) + { ++ if (!i40evf_driver_selected(pci_dev->device.devargs)) ++ return 1; ++ + return rte_eth_dev_pci_generic_probe(pci_dev, + sizeof(struct i40e_adapter), i40evf_dev_init); + } +@@ -1617,6 +1661,7 @@ static struct rte_pci_driver rte_i40evf_pmd = { + RTE_PMD_REGISTER_PCI(net_i40e_vf, rte_i40evf_pmd); + RTE_PMD_REGISTER_PCI_TABLE(net_i40e_vf, pci_id_i40evf_map); + RTE_PMD_REGISTER_KMOD_DEP(net_i40e_vf, "* igb_uio | vfio-pci"); ++RTE_PMD_REGISTER_PARAM_STRING(net_i40e_vf, "driver=i40evf"); + + static int + i40evf_dev_configure(struct rte_eth_dev *dev) +diff --git a/drivers/net/iavf/iavf_ethdev.c b/drivers/net/iavf/iavf_ethdev.c +index c3aa4cd725..f22c3ccdb9 100644 +--- a/drivers/net/iavf/iavf_ethdev.c ++++ b/drivers/net/iavf/iavf_ethdev.c +@@ -76,6 +76,10 @@ static int iavf_dev_filter_ctrl(struct rte_eth_dev *dev, + + static const struct rte_pci_id pci_id_iavf_map[] = { + { RTE_PCI_DEVICE(IAVF_INTEL_VENDOR_ID, IAVF_DEV_ID_ADAPTIVE_VF) }, ++ { RTE_PCI_DEVICE(IAVF_INTEL_VENDOR_ID, IAVF_DEV_ID_VF) }, ++ { RTE_PCI_DEVICE(IAVF_INTEL_VENDOR_ID, IAVF_DEV_ID_VF_HV) }, ++ { RTE_PCI_DEVICE(IAVF_INTEL_VENDOR_ID, IAVF_DEV_ID_X722_VF) }, ++ { RTE_PCI_DEVICE(IAVF_INTEL_VENDOR_ID, IAVF_DEV_ID_X722_A0_VF) }, + { .vendor_id = 0, /* sentinel */ }, + }; + +@@ -1516,10 +1520,59 @@ iavf_dcf_cap_selected(struct rte_devargs *devargs) + return ret; + } + ++static int ++iavf_drv_i40evf_check_handler(__rte_unused const char *key, ++ const char *value, __rte_unused void *opaque) ++{ ++ if (strcmp(value, "i40evf")) ++ return -1; ++ ++ return 0; ++} ++ ++static int ++iavf_drv_i40evf_selected(struct rte_devargs *devargs, uint16_t device_id) ++{ ++ struct rte_kvargs *kvlist; ++ const char *key = "driver"; ++ int ret = 0; ++ ++ if (device_id != IAVF_DEV_ID_VF && ++ device_id != IAVF_DEV_ID_VF_HV && ++ device_id != IAVF_DEV_ID_X722_VF && ++ device_id != IAVF_DEV_ID_X722_A0_VF) ++ return 0; ++ ++ if (devargs == NULL) ++ return 0; ++ ++ kvlist = rte_kvargs_parse(devargs->args, NULL); ++ if (kvlist == NULL) ++ return 0; ++ ++ if (!rte_kvargs_count(kvlist, key)) ++ goto exit; ++ ++ /* i40evf driver selected when there's a key-value pair: ++ * driver=i40evf ++ */ ++ if (rte_kvargs_process(kvlist, key, ++ iavf_drv_i40evf_check_handler, NULL) < 0) ++ goto exit; ++ ++ ret = 1; ++ ++exit: ++ rte_kvargs_free(kvlist); ++ return ret; ++} ++ + static int eth_iavf_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, + struct rte_pci_device *pci_dev) + { +- if (iavf_dcf_cap_selected(pci_dev->device.devargs)) ++ if (iavf_dcf_cap_selected(pci_dev->device.devargs) || ++ iavf_drv_i40evf_selected(pci_dev->device.devargs, ++ pci_dev->id.device_id)) + return 1; + + return rte_eth_dev_pci_generic_probe(pci_dev, +@@ -1542,7 +1595,7 @@ static struct rte_pci_driver rte_iavf_pmd = { + RTE_PMD_REGISTER_PCI(net_iavf, rte_iavf_pmd); + RTE_PMD_REGISTER_PCI_TABLE(net_iavf, pci_id_iavf_map); + RTE_PMD_REGISTER_KMOD_DEP(net_iavf, "* igb_uio | vfio-pci"); +-RTE_PMD_REGISTER_PARAM_STRING(net_iavf, "cap=dcf"); ++RTE_PMD_REGISTER_PARAM_STRING(net_iavf, "cap=dcf driver=i40evf"); + RTE_LOG_REGISTER(iavf_logtype_init, pmd.net.iavf.init, NOTICE); + RTE_LOG_REGISTER(iavf_logtype_driver, pmd.net.iavf.driver, NOTICE); + #ifdef RTE_LIBRTE_IAVF_DEBUG_RX +-- +2.20.1 + diff --git a/build/external/patches/dpdk_20.08/0003-backport-dpdk-usertools-support-python-3-only.patch b/build/external/patches/dpdk_20.08/0003-backport-dpdk-usertools-support-python-3-only.patch new file mode 100644 index 000000000000..7cc718306aa1 --- /dev/null +++ b/build/external/patches/dpdk_20.08/0003-backport-dpdk-usertools-support-python-3-only.patch @@ -0,0 +1,212 @@ +From 858b4575513fe72dce95370944b0da237b755204 Mon Sep 17 00:00:00 2001 +From: Dave Wallace +Date: Thu, 15 Oct 2020 15:22:22 -0400 +Subject: [PATCH] backport dpdk usertools support python 3 only + +Applicable usertools section of DPDK patch: +http://git.dpdk.org/dpdk/commit/?id=3f6f83626cf4967a99382a6518a614a1bf3d2c20 + +Required to avoid build failure of 'make install-ext-deps' on CentOS-8 due +to brp-mangle-shebangs failing on un-versioned python shebang (e.g. +'#! /usr/bin/env python'. + +Signed-off-by: Dave Wallace +--- + usertools/cpu_layout.py | 15 ++------------- + usertools/dpdk-devbind.py | 22 ++++------------------ + usertools/dpdk-pmdinfo.py | 7 +------ + usertools/dpdk-telemetry-client.py | 18 +++--------------- + usertools/dpdk-telemetry.py | 2 +- + 5 files changed, 11 insertions(+), 53 deletions(-) + +diff --git a/usertools/cpu_layout.py b/usertools/cpu_layout.py +index 5423c7965..cc3963821 100755 +--- a/usertools/cpu_layout.py ++++ b/usertools/cpu_layout.py +@@ -1,19 +1,8 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + # SPDX-License-Identifier: BSD-3-Clause + # Copyright(c) 2010-2014 Intel Corporation + # Copyright(c) 2017 Cavium, Inc. All rights reserved. + +-from __future__ import print_function +-import sys +-try: +- xrange # Python 2 +-except NameError: +- xrange = range # Python 3 +- +-if sys.version_info.major < 3: +- print("WARNING: Python 2 is deprecated for use in DPDK, and will not work in future releases.", file=sys.stderr) +- print("Please use Python 3 instead", file=sys.stderr) +- + sockets = [] + cores = [] + core_map = {} +@@ -21,7 +10,7 @@ + fd = open("{}/kernel_max".format(base_path)) + max_cpus = int(fd.read()) + fd.close() +-for cpu in xrange(max_cpus + 1): ++for cpu in range(max_cpus + 1): + try: + fd = open("{}/cpu{}/topology/core_id".format(base_path, cpu)) + except IOError: +diff --git a/usertools/dpdk-devbind.py b/usertools/dpdk-devbind.py +index 094c2ffc8..8278a748d 100755 +--- a/usertools/dpdk-devbind.py ++++ b/usertools/dpdk-devbind.py +@@ -1,4 +1,4 @@ +-#! /usr/bin/env python ++#! /usr/bin/env python3 + # SPDX-License-Identifier: BSD-3-Clause + # Copyright(c) 2010-2014 Intel Corporation + # + +-from __future__ import print_function + import sys + import os + import getopt +@@ -12,10 +11,6 @@ + from os.path import exists, abspath, dirname, basename + from os.path import join as path_join + +-if sys.version_info.major < 3: +- print("WARNING: Python 2 is deprecated for use in DPDK, and will not work in future releases.", file=sys.stderr) +- print("Please use Python 3 instead", file=sys.stderr) +- + # The PCI base class for all devices + network_class = {'Class': '02', 'Vendor': None, 'Device': None, + 'SVendor': None, 'SDevice': None} +@@ -154,14 +149,6 @@ def usage(): + + """ % 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] +- + # check if a specific kernel module is loaded + def module_is_loaded(module): + global loaded_modules +@@ -218,8 +205,7 @@ def get_pci_device_details(dev_id, probe_lspci): + device = {} + + if probe_lspci: +- extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines() +- ++ extra_info = subprocess.check_output(["lspci", "-vmmks", dev_id]).splitlines() + # parse lspci details + for line in extra_info: + if len(line) == 0: +@@ -255,7 +241,7 @@ def get_device_details(devices_type): + # first loop through and read details for all devices + # request machine readable format, with numeric IDs and String + dev = {} +- dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines() ++ dev_lines = subprocess.check_output(["lspci", "-Dvmmnnk"]).splitlines() + for dev_line in dev_lines: + if len(dev_line) == 0: + if device_type_match(dev, devices_type): +@@ -283,7 +269,7 @@ def get_device_details(devices_type): + # check what is the interface if any for an ssh connection if + # any to this host, so we can mark it later. + ssh_if = [] +- route = check_output(["ip", "-o", "route"]) ++ route = subprocess.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())) +diff --git a/usertools/dpdk-pmdinfo.py b/usertools/dpdk-pmdinfo.py +index f9ed75517..166198279 100755 +--- a/usertools/dpdk-pmdinfo.py ++++ b/usertools/dpdk-pmdinfo.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + # SPDX-License-Identifier: BSD-3-Clause + # Copyright(c) 2016 Neil Horman + +@@ -7,8 +7,6 @@ + # Utility to dump PMD_INFO_STRING support from an object file + # + # ------------------------------------------------------------------------- +-from __future__ import print_function +-from __future__ import unicode_literals + import json + import io + import os +@@ -28,9 +26,6 @@ + pcidb = None + + # =========================================== +-if sys.version_info.major < 3: +- print("WARNING: Python 2 is deprecated for use in DPDK, and will not work in future releases.", file=sys.stderr) +- print("Please use Python 3 instead", file=sys.stderr) + + class Vendor: + """ +diff --git a/usertools/dpdk-telemetry-client.py b/usertools/dpdk-telemetry-client.py +index 98d28fa89..d8e439027 100755 +--- a/usertools/dpdk-telemetry-client.py ++++ b/usertools/dpdk-telemetry-client.py +@@ -1,10 +1,7 @@ +-#! /usr/bin/env python ++#! /usr/bin/env python3 + # SPDX-License-Identifier: BSD-3-Clause + # Copyright(c) 2018 Intel Corporation + +-from __future__ import print_function +-from __future__ import unicode_literals +- + import socket + import os + import sys +@@ -18,15 +15,6 @@ + GLOBAL_METRICS_REQ = "{\"action\":0,\"command\":\"global_stat_values\",\"data\":null}" + DEFAULT_FP = "/var/run/dpdk/default_client" + +-try: +- raw_input # Python 2 +-except NameError: +- raw_input = input # Python 3 +- +-if sys.version_info.major < 3: +- print("WARNING: Python 2 is deprecated for use in DPDK, and will not work in future releases.", file=sys.stderr) +- print("Please use Python 3 instead", file=sys.stderr) +- + class Socket: + + def __init__(self): +@@ -86,7 +74,7 @@ def requestMetrics(self): # Requests metrics for given client + + def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client + print("\nPlease enter the number of times you'd like to continuously request Metrics:") +- n_requests = int(raw_input("\n:")) ++ n_requests = int(input("\n:")) + print("\033[F") #Removes the user input from screen, cleans it up + print("\033[K") + for i in range(n_requests): +@@ -107,7 +95,7 @@ def interactiveMenu(self, sleep_time): # Creates Interactive menu within the scr + print("[4] Unregister client") + + try: +- self.choice = int(raw_input("\n:")) ++ self.choice = int(input("\n:")) + print("\033[F") #Removes the user input for screen, cleans it up + print("\033[K") + if self.choice == 1: +diff --git a/usertools/dpdk-telemetry.py b/usertools/dpdk-telemetry.py +index 8e4039d57..181859658 100755 +--- a/usertools/dpdk-telemetry.py ++++ b/usertools/dpdk-telemetry.py +@@ -1,4 +1,4 @@ +-#! /usr/bin/python3 ++#! /usr/bin/env python3 + # SPDX-License-Identifier: BSD-3-Clause + # Copyright(c) 2020 Intel Corporation diff --git a/build/external/rpm/vpp-ext-deps.spec b/build/external/rpm/vpp-ext-deps.spec index e0980abe0721..956ea68d97bc 100644 --- a/build/external/rpm/vpp-ext-deps.spec +++ b/build/external/rpm/vpp-ext-deps.spec @@ -1,6 +1,10 @@ %define _install_dir /opt/vpp/external/%(uname -m) %define _make_args -C ../.. BUILD_DIR=%{_topdir}/tmp INSTALL_DIR=%{buildroot}%{_install_dir} +%{!?__python3: %global __python3 /usr/bin/python3} +%global __python %{__python3} +%global _pylib /usr/lib/python3.6/site-packages + Name: vpp-ext-deps Version: %{_version} Release: %{_release} diff --git a/docs/Makefile b/docs/Makefile index 6c5960d18c48..49938640a349 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -27,6 +27,8 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile +# Generate dynamic content + @python3 ./includes_renderer.py @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) spell: diff --git a/docs/dynamic_includes/.gitkeep b/docs/dynamic_includes/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/gettingstarted/index.rst b/docs/gettingstarted/index.rst index 2efe372b4dc2..08fc1cd329ee 100644 --- a/docs/gettingstarted/index.rst +++ b/docs/gettingstarted/index.rst @@ -11,7 +11,7 @@ code that provides tools that are used in a development environment. This section covers the following: -* Describes how to manually install VPP Binaries on different OS platforms (Ubuntu, Centos, openSUSE) and then how to configure and use VPP. +* Describes how to manually install VPP Binaries on different OS platforms (Ubuntu, Centos) and then how to configure and use VPP. * Describes the different types of VPP packages, which are used in both basic and developer installs. * A VPP tutorial which is a great way to learn VPP basics. diff --git a/docs/gettingstarted/installing/index.rst b/docs/gettingstarted/installing/index.rst index 6326247e95a8..b482ea651466 100644 --- a/docs/gettingstarted/installing/index.rst +++ b/docs/gettingstarted/installing/index.rst @@ -10,8 +10,8 @@ Downloading and Installing VPP If you want to use VPP it can be convenient to install the binaries from existing packages. This guide describes how to pull, install and run the VPP packages. -This section provides directions on how to Install VPP binaries on Ubuntu, Centos, -and openSUSE platforms. +This section provides directions on how to Install VPP binaries on +Ubuntu, and Centos platforms. FD.io VPP is installed using Package Cloud. For a complete set of instructions on how to install VPP with package cloud please refer @@ -36,15 +36,6 @@ The following are instructions on how to install VPP on Centos. centos -Installing on openSUSE --------------------------------------- - -The following are instructions on how to install VPP on openSUSE. - -.. toctree:: - - opensuse - Package Descriptions ---------------------------------- diff --git a/docs/gettingstarted/installing/opensuse.rst b/docs/gettingstarted/installing/opensuse.rst deleted file mode 100644 index 6d7873820dc3..000000000000 --- a/docs/gettingstarted/installing/opensuse.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. _opensuse: - -.. toctree:: - -Installing -========== - -To install VPP on openSUSE, first install the following release, and then execute -the associated commands. - -openSUSE Tumbleweed (rolling release) ------------------------------------------------------------- - -.. code-block:: console - - sudo zypper install vpp vpp-plugins - -openSUSE Leap 42.3 --------------------------------- - -.. code-block:: console - - sudo zypper addrepo --name network https://download.opensuse.org/repositories/network/openSUSE_Leap_42.3/network.repo - sudo zypper install vpp vpp-plugins - -Uninstall -========= - -To uninstall the vpp plugins, run the following command: - -.. code-block:: console - - sudo zypper remove -u vpp vpp-plugins - -openSUSE Tumbleweed (rolling release) -------------------------------------- - -To uninstall the openSUSE Tumbleweed, run the following command: - -.. code-block:: console - - sudo zypper remove -u vpp vpp-plugins - -openSUSE Leap 42.3 ------------------- - -.. code-block:: console - - sudo zypper remove -u vpp vpp-plugins - sudo zypper removerepo network - -For More Information -==================== -For more information on VPP with openSUSE, please look at the following post. - -* https://www.suse.com/communities/blog/vector-packet-processing-vpp-opensuse/ - diff --git a/docs/gettingstarted/installing/packages.rst b/docs/gettingstarted/installing/packages.rst index 719fffb67f88..6c4fae18ee33 100644 --- a/docs/gettingstarted/installing/packages.rst +++ b/docs/gettingstarted/installing/packages.rst @@ -35,19 +35,7 @@ vpp-plugins Vector Packet Processing plugin modules. -* acl -* dpdk -* flowprobe -* gtpu -* ixge -* kubeproxy -* l2e -* lb -* memif -* nat -* pppoe -* sixrd -* stn +.. include:: ../../dynamic_includes/plugin_list.inc vpp-dbg ------- diff --git a/docs/gettingstarted/installing/ubuntu.rst b/docs/gettingstarted/installing/ubuntu.rst index 98d73541c4e1..2521b6d27908 100644 --- a/docs/gettingstarted/installing/ubuntu.rst +++ b/docs/gettingstarted/installing/ubuntu.rst @@ -1,11 +1,11 @@ .. _ubuntu: .. toctree:: - -Ubuntu 18.04 - Setup the FD.io Repository -========================================== -Choose one of the following releases to install. +Ubuntu - Setup the FD.io Repository +=================================== + +Choose one of the following releases to install. Update the OS ----------------------- @@ -85,4 +85,4 @@ Uninstall the packages by running the following command: .. code-block:: console - sudo apt-get remove --purge vpp* + sudo apt-get remove --purge "vpp*" diff --git a/docs/includes_renderer.py b/docs/includes_renderer.py new file mode 100644 index 000000000000..d36752dd882d --- /dev/null +++ b/docs/includes_renderer.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020. Vinci Consulting Corp. All Rights Reserved. +# +# 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 glob +import inspect +import os.path +import re + + +class ContentRenderer: + name = "" + curr_path = os.path.abspath(inspect.getsourcefile(lambda: 0)) + vpp_root = curr_path.rsplit("/", 2)[0] + output_dir = f"{vpp_root}/docs/dynamic_includes/" + + def render(self): + raise NotImplementedError + + +class PluginRenderer(ContentRenderer): + name = "plugin_list.inc" + + plugin_dir = f"{ContentRenderer.vpp_root}/src/plugins" + + pattern = r'VLIB_PLUGIN_REGISTER\s?\(\)\s*=\s*{.*\.description\s?=\s?"([^"]*)".*};' # noqa: 501 + regex = re.compile(pattern, re.MULTILINE | re.DOTALL) + + def render(self): + with open(f"{self.__class__.output_dir}{self.__class__.name}", + "w") as output: + with os.scandir(self.__class__.plugin_dir) as pdir: + for entry in sorted(pdir, key=lambda entry: entry.name): + if not entry.name.startswith('.') and entry.is_dir(): + description = "" + # we use glob because a plugin can (ioam for now) + # define the plugin definition in + # a further subdirectory. + for f in glob.iglob(f'{self.__class__.plugin_dir}/' + f'{entry.name}/**', + recursive=True): + if f.endswith('.c'): + with open(f, "r", encoding="utf-8") \ + as src: + for match in self.__class__.regex.finditer( + src.read()): + description = "%s" % (match.group(1)) + + output.write(f"* {entry.name} - {description}\n") + + +# if this list grows substantially, we can move the classes to +# a folder and import them. +renderers = [PluginRenderer, + ] + + +def main(): + print("rendering dynamic includes...") + for renderer in renderers: + renderer().render() + print("done.") + + +if __name__ == "__main__": + main() diff --git a/docs/reference/vppvagrant/Vagrantfile b/docs/reference/vppvagrant/Vagrantfile index 457e37c82741..642969faa630 100644 --- a/docs/reference/vppvagrant/Vagrantfile +++ b/docs/reference/vppvagrant/Vagrantfile @@ -9,9 +9,6 @@ Vagrant.configure(2) do |config| config.vm.box = "centos/7" config.vm.box_version = "1708.01" config.ssh.insert_key = false - elsif distro == 'opensuse' - config.vm.box = "opensuse/openSUSE-42.3-x86_64" - config.vm.box_version = "1.0.4.20170726" else config.vm.box = "puppetlabs/ubuntu-16.04-64-nocm" end diff --git a/docs/reference/vppvagrant/boxSetup.rst b/docs/reference/vppvagrant/boxSetup.rst index a8aa1f3c1321..ed20c9d37c16 100644 --- a/docs/reference/vppvagrant/boxSetup.rst +++ b/docs/reference/vppvagrant/boxSetup.rst @@ -34,9 +34,6 @@ Looking at the :ref:`vppVagrantfile`, we can see that the default OS is Ubuntu 1 config.vm.box = "centos/7" config.vm.box_version = "1708.01" config.ssh.insert_key = false - elsif distro == 'opensuse' - config.vm.box = "opensuse/openSUSE-42.3-x86_64" - config.vm.box_version = "1.0.4.20170726" else config.vm.box = "puppetlabs/ubuntu-16.04-64-nocm" diff --git a/docs/usecases/index.rst b/docs/usecases/index.rst index 062b225732b3..3b2ab23f827b 100644 --- a/docs/usecases/index.rst +++ b/docs/usecases/index.rst @@ -20,3 +20,4 @@ extensive list, but should give a sampling of the many features contained in FD. networksim webapp container_test + trafficgen diff --git a/docs/usecases/trafficgen.md b/docs/usecases/trafficgen.md new file mode 100644 index 000000000000..fe3d4c98904f --- /dev/null +++ b/docs/usecases/trafficgen.md @@ -0,0 +1,105 @@ +Vpp Stateless Traffic Generation +================================ + +It's simple to configure vpp as a high-performance stateless traffic +generator. A couple of vpp worker threads running on an older system +can easily generate 20 MPPS' worth of traffic. + +In the configurations shown below, we connect a vpp traffic generator +and a vpp UUT using two 40 gigabit ethernet ports on each system: + +``` + +-------------------+ +-------------------+ + | traffic generator | | UUT | + | port 0 | <=======> | port 0 | + | 192.168.40.2/24 | | 192.168.40.1/24 | + +-------------------+ +-------------------+ + + +-------------------+ +-------------------+ + | traffic generator | | UUT | + | port 1 | <=======> | port 1 | + | 192.168.41.2/24 | | 192.168.41.1/24 | + +-------------------+ +-------------------+ +``` + +Traffic Generator Setup Script +------------------------------ + +``` + set int ip address FortyGigabitEthernet2/0/0 192.168.40.2/24 + set int ip address FortyGigabitEthernet2/0/1 192.168.41.2/24 + set int state FortyGigabitEthernet2/0/0 up + set int state FortyGigabitEthernet2/0/1 up + + comment { send traffic to the VPP UUT } + + packet-generator new { + name worker0 + worker 0 + limit 0 + rate 1.2e7 + size 128-128 + tx-interface FortyGigabitEthernet2/0/0 + node FortyGigabitEthernet2/0/0-output + data { IP4: 1.2.40 -> 3cfd.fed0.b6c8 + UDP: 192.168.40.10 -> 192.168.50.10 + UDP: 1234 -> 2345 + incrementing 114 + } + } + + packet-generator new { + name worker1 + worker 1 + limit 0 + rate 1.2e7 + size 128-128 + tx-interface FortyGigabitEthernet2/0/1 + node FortyGigabitEthernet2/0/1-output + data { IP4: 1.2.4 -> 3cfd.fed0.b6c9 + UDP: 192.168.41.10 -> 192.168.51.10 + UDP: 1234 -> 2345 + incrementing 114 + } + } + + comment { delete return traffic on sight } + + ip route add 192.168.50.0/24 via drop + ip route add 192.168.51.0/24 via drop +``` + +Note 1: the destination MAC addresses shown in the configuration (e.g. +3cfd.fed0.b6c8 and 3cfd.fed0.b6c9) **must** match the vpp UUT port MAC +addresses. + +Note 2: this script assumes that /etc/vpp/startup.conf and/or the +command-line in use specifies (at least) two worker threads. Uncomment +"workers 2" in the cpu configuration section of /etc/vpp/startup.conf: + +``` + ## Specify a number of workers to be created + ## Workers are pinned to N consecutive CPU cores while skipping "skip-cores" CPU core(s) + ## and main thread's CPU core + workers 2 +``` + +Any plausible packet generator script - including one which replays +pcap captures - can be used. + + +UUT Setup Script +---------------- + +The vpp UUT uses a couple of static routes to forward traffic back to +the traffic generator: + +``` + set int ip address FortyGigabitEthernet2/0/0 192.168.40.1/24 + set int ip address FortyGigabitEthernet2/0/1 192.168.41.1/24 + set int state FortyGigabitEthernet2/0/0 up + set int state FortyGigabitEthernet2/0/1 up + + ip route add 192.168.50.10/32 via 192.168.41.2 + ip route add 192.168.51.10/32 via 192.168.40.2 +``` diff --git a/doxygen/Makefile b/doxygen/Makefile index 40646148ec6a..e399b404a260 100644 --- a/doxygen/Makefile +++ b/doxygen/Makefile @@ -35,7 +35,6 @@ OS_ID ?= $(shell grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g') # Package dependencies DOC_DEB_DEPENDS = doxygen graphviz python3-pyparsing python3-jinja2 DOC_RPM_DEPENDS = doxygen graphviz python3-pyparsing python3-jinja2 -DOC_SUSE_RPM_DEPENDS = doxygen graphviz python3-pyparsing python3-Jinja2 DOC_MAC_BIN_DEPENDS = doxygen dot git DOC_MAC_PY_DEPENDS = pyparsing jinja2 @@ -156,8 +155,6 @@ else ifeq ($(OS_ID),darwin) false; \ ); \ done -else ifeq ($(OS_ID),opensuse) - @sudo zypper install $(CONFIRM) $(DOC_SUSE_RPM_DEPENDS) else $(error "Building documentation currently works only on Ubuntu, CentOS, MacOS and OpenSUSE systems.") endif diff --git a/doxygen/user_doc.md b/doxygen/user_doc.md index 009af67cf1a7..0610adc74583 100644 --- a/doxygen/user_doc.md +++ b/doxygen/user_doc.md @@ -4,24 +4,22 @@ User Documentation {#user_doc} Several modules provide operational, dataplane-user focused documentation. - [GUI guided user demo](https://wiki.fd.io/view/VPP_Sandbox/vpp-userdemo) +- @subpage af_xdp_doc - @subpage avf_plugin_doc - @subpage bfd_doc - @subpage dpdk_crypto_ipsec_doc - @subpage dhcp6_pd_doc - @subpage flowprobe_plugin_doc - @subpage ioam_plugin_doc -- @subpage kp_plugin_doc - @subpage lacp_plugin_doc - @subpage lb_plugin_doc - @subpage lldp_doc - @subpage map_doc - @subpage marvel_plugin_doc -- @subpage srv6_mobile_plugin - @subpage mtu_doc - @subpage nat64_doc - @subpage nat_ha_doc - @subpage qos_doc -- @subpage quic_doc - @subpage rdma_doc - @subpage selinux_doc - @subpage span_doc @@ -29,3 +27,4 @@ Several modules provide operational, dataplane-user focused documentation. - @subpage srv6_doc - @subpage vcl_ldpreload_doc - @subpage vmxnet3_doc +- @subpage wireguard_plugin_doc diff --git a/extras/rpm/Makefile b/extras/rpm/Makefile index 423c4207b020..0736b7f6a4b0 100644 --- a/extras/rpm/Makefile +++ b/extras/rpm/Makefile @@ -26,18 +26,7 @@ PC=% all: RPM -# SUSE rolling-release (a.k.a. Tumbleweed) -ifeq ($(filter opensuse-tumbleweed,$(OS_ID)),$(OS_ID)) -SPEC_FILE='vpp-suse.spec' -# SUSE osleap15 -else ifeq ($(filter opensuse-leap,$(OS_ID)),$(OS_ID)) -SPEC_FILE='vpp-suse.spec' -# SUSE leap42.x -else ifeq ($(filter opensuse,$(OS_ID)),$(OS_ID)) -SPEC_FILE='vpp-suse.spec' -else SPEC_FILE='vpp.spec' -endif spec: @echo $(TARBALL) diff --git a/extras/rpm/vpp-suse.spec b/extras/rpm/vpp-suse.spec deleted file mode 100644 index 4bb354cda90b..000000000000 --- a/extras/rpm/vpp-suse.spec +++ /dev/null @@ -1,325 +0,0 @@ -# -# spec file for package vpp -# -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. -# -# 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. - -# Please submit bugfixes or comments via http://bugs.opensuse.org/ -# - - -%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/lib - -%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: check-devel -BuildRequires: chrpath -BuildRequires: distribution-release -BuildRequires: gcc -BuildRequires: gcc-c++ -BuildRequires: glibc-devel -BuildRequires: glibc-devel-static -BuildRequires: libboost_headers-devel -BuildRequires: libboost_thread-devel -BuildRequires: libnuma-devel -BuildRequires: libopenssl-devel -BuildRequires: libtool -BuildRequires: lsb-release -BuildRequires: make -BuildRequires: mbedtls-devel -BuildRequires: openssl-devel -BuildRequires: python-devel -BuildRequires: python-pip -BuildRequires: python-rpm-macros -BuildRequires: python2-ply -BuildRequires: python3-devel -BuildRequires: python3-pip -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: python-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} - -%build -export VPP_BUILD_USER=suse -export VPP_BUILD_HOST=SUSE - -make -C build-root V=1 CC=gcc-7 CXX=g++-7 PLATFORM=vpp TAG=vpp install-packages - -cd %{_vpp_build_dir}/../src/vpp-api/python && %{py2_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 -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 && %{py2_install} - -mkdir -p -m755 %{buildroot}%{python_sitelib}/vpp_papi -for file in $(find %{_vpp_install_dir}/*/lib/python2.7/site-packages/ -type f -print | grep -v pyc | grep -v pyo) -do - install -p -m755 $file %{buildroot}%{python_sitelib}/vpp_papi/ -done -chmod -x %{buildroot}%{python_sitelib}/vpp_papi/*.txt - -# -# devel -# -for dir in %{_vpp_install_dir}/{vom,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* -%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 %{python_sitelib}/vpp_papi* -%{python_sitelib}/vpp_papi* - -%files devel -%dir %{_datadir}/doc/vpp -%dir %{_datadir}/doc/vpp/examples -%{_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 538c071caea3..1bb78af8cf7a 100644 --- a/extras/rpm/vpp.spec +++ b/extras/rpm/vpp.spec @@ -60,7 +60,6 @@ BuildRequires: python3, python36-devel, python3-virtualenv BuildRequires: cmake %else %if 0%{rhel} >= 7 -Requires: epel-release Requires: vpp-lib = %{_version}-%{_release}, vpp-selinux-policy = %{_version}-%{_release}, net-tools, pciutils, python36 Requires: boost-filesystem mbedtls libffi-devel BuildRequires: epel-release @@ -150,7 +149,7 @@ This package contains the python bindings for the vpp api Summary: VPP api python3 bindings Group: Development/Libraries Requires: vpp = %{_version}-%{_release}, vpp-lib = %{_version}-%{_release}, libffi-devel -Requires: python-setuptools +Requires: python3-setuptools %description api-python3 This package contains the python3 bindings for the vpp api @@ -169,7 +168,10 @@ Requires(post): python3-policycoreutils This package contains a tailored VPP SELinux policy %prep -%setup -q -n %{name}-%{_version} +%setup -q -c -T -n %{name}-%{_version} +cd .. +unxz --stdout ./SOURCES/%{name}-%{_version}-%{_release}.tar.xz | tar --extract --touch +cd - %pre # Add the vpp group @@ -180,7 +182,7 @@ groupadd -f -r vpp . /opt/rh/devtoolset-9/enable %endif %if %{with aesni} - make bootstrap + make install-dep make -C build-root PLATFORM=vpp TAG=%{_vpp_tag} install-packages %else make bootstrap AESNI=n diff --git a/extras/scripts/crcchecker.py b/extras/scripts/crcchecker.py index 7f83d2e6d856..2b026338129f 100755 --- a/extras/scripts/crcchecker.py +++ b/extras/scripts/crcchecker.py @@ -96,19 +96,34 @@ def filelist_from_patchset(): return set(filelist) def is_deprecated(d, k): - if 'options' in d[k] and 'deprecated' in d[k]['options']: - return True + if 'options' in d[k]: + if 'deprecated' in d[k]['options']: + return True + # recognize the deprecated format + if 'status' in d[k]['options'] and d[k]['options']['status'] == 'deprecated': + print("WARNING: please use 'option deprecated;'") + return True return False def is_in_progress(d, k): - try: - if d[k]['options']['status'] == 'in_progress': + if 'options' in d[k]: + if 'in_progress' in d[k]['options']: + return True + # recognize the deprecated format + if 'status' in d[k]['options'] and d[k]['options']['status'] == 'in_progress': + print("WARNING: please use 'option in_progress;'") return True - except: - return False + return False -def report(old, new, added, removed, modified, same): +def report(new, old): + added, removed, modified, same = 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']) + if newversion == 0 or is_in_progress(new, k): + print(f'in-progress: {k}') for k in added: print(f'added: {k}') for k in removed: @@ -126,6 +141,17 @@ def report(old, new, added, removed, modified, same): print(f'modified: ** {k}') else: 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']) + if newversion > 0 and is_deprecated(new, k): + if k in old: + if not is_deprecated(old, k): + print(f'deprecated: {k}') + else: + print(f'added+deprecated: {k}') + return backwards_incompatible @@ -150,8 +176,7 @@ def main(): if args.diff: oldcrcs = crc_from_apigen(None, args.diff[0]) newcrcs = crc_from_apigen(None, args.diff[1]) - added, removed, modified, same = dict_compare(newcrcs, oldcrcs) - backwards_incompatible = report(oldcrcs, newcrcs, added, removed, modified, same) + backwards_incompatible = report(newcrcs, oldcrcs) sys.exit(0) # Dump CRC for messages in given files / revision @@ -186,8 +211,7 @@ def main(): newcrcs.update(crc_from_apigen(None, f)) oldcrcs.update(crc_from_apigen(revision, f)) - added, removed, modified, same = dict_compare(newcrcs, oldcrcs) - backwards_incompatible = report(oldcrcs, newcrcs, added, removed, modified, same) + backwards_incompatible = report(newcrcs, oldcrcs) if args.check_patchset: if backwards_incompatible: diff --git a/extras/scripts/tests/test_crcchecker.sh b/extras/scripts/tests/test_crcchecker.sh index 07b6bbf7f4b7..9cfc66ae5232 100755 --- a/extras/scripts/tests/test_crcchecker.sh +++ b/extras/scripts/tests/test_crcchecker.sh @@ -77,6 +77,29 @@ git add crccheck.api git commit -m "deprecated api"; extras/scripts/crcchecker.py --check-patchset +echo "TEST 7.1: Verify we can delete deprecated message (old/confused style)" +cat >crccheck_dep.api <crccheck_dep.api < "Darwin" ]; then +if [ "$(uname)" <> "Darwin" ] ; then OS_ID=$(grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g') OS_VERSION_ID=$(grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g') fi -if [ "$OS_ID" == "ubuntu" ]; then +if [ "$OS_ID" == "ubuntu" ] ; then $OS_CODENAME=$UBUNTU_CODENAME fi -if [ "$OS_ID" == "centos" ] || [ "$OS_ID" == "opensuse" ]; then +if [ "$OS_ID" == "centos" ] ; then # Install uio-pci-generic sudo -E modprobe uio_pci_generic fi diff --git a/src/pkg/debian/control.in b/src/pkg/debian/control.in index 0b0c621e50c6..ef66682f8a7d 100644 --- a/src/pkg/debian/control.in +++ b/src/pkg/debian/control.in @@ -51,6 +51,9 @@ Description: Vector Packet Processing--runtime libraries Package: vpp-plugin-core Architecture: any Depends: vpp (= ${source:Version}), + libmbedtls12 | libmbedtls10, + libmbedx509-0, + libmbedcrypto3 | libmbedcrypto1 | libmbedcrypto0, ${shlibs:Depends} Description: Vector Packet Processing--runtime core plugins This package contains VPP core plugins @@ -76,6 +79,9 @@ Description: VPP Python API bindings Package: python3-vpp-api Architecture: any Depends: vpp (= ${source:Version}), + python3-cffi, + python3-ply, + python3-pycparser, ${python3:Depends}, ${misc:Depends} Description: VPP Python3 API bindings diff --git a/src/plugins/af_xdp/af_xdp.api b/src/plugins/af_xdp/af_xdp.api index 765af6820e3d..14f51d87d6ab 100644 --- a/src/plugins/af_xdp/af_xdp.api +++ b/src/plugins/af_xdp/af_xdp.api @@ -31,7 +31,7 @@ enum af_xdp_mode @param context - sender context, to match reply w/ request @param host_if - Linux netdev interface name @param name - new af_xdp interface name (optional) - @param rxq_num - number of receive queues (optional) + @param rxq_num - number of receive queues. 65535 can be used as special value to request all available queues (optional) @param rxq_size - receive queue size (optional) @param txq_size - transmit queue size (optional) @param mode - operation mode (optional) @@ -50,7 +50,7 @@ define af_xdp_create u16 txq_size [default=0]; vl_api_af_xdp_mode_t mode [default=0]; string prog[256]; - option vat_help = " [name ifname] [rx-queue-size size] [tx-queue-size size] [num-rx-queues num] [prog pathname] [zero-copy|no-zero-copy]"; + option vat_help = " [name ifname] [rx-queue-size size] [tx-queue-size size] [num-rx-queues ] [prog pathname] [zero-copy|no-zero-copy]"; option status="in_progress"; }; diff --git a/src/plugins/af_xdp/af_xdp.h b/src/plugins/af_xdp/af_xdp.h index fd990ec3f90c..3bd53ad768d2 100644 --- a/src/plugins/af_xdp/af_xdp.h +++ b/src/plugins/af_xdp/af_xdp.h @@ -22,6 +22,8 @@ #include #include +#define AF_XDP_NUM_RX_QUEUES_ALL ((u16)-1) + #define af_xdp_log(lvl, dev, f, ...) \ vlib_log(lvl, af_xdp_main.log_class, "%v: " f, (dev)->name, ##__VA_ARGS__) diff --git a/src/plugins/af_xdp/af_xdp_doc.md b/src/plugins/af_xdp/af_xdp_doc.md index 6d2dae550557..76d653fd251d 100644 --- a/src/plugins/af_xdp/af_xdp_doc.md +++ b/src/plugins/af_xdp/af_xdp_doc.md @@ -17,6 +17,13 @@ Because of AF_XDP restrictions, the MTU is limited to below PAGE_SIZE (4096-bytes on most systems) minus 256-bytes, and they are additional limitations depending upon specific Linux device drivers. As a rule of thumb, a MTU of 3000-bytes or less should be safe. +Furthermore, upon UMEM creation, the kernel allocates a +physically-contiguous structure, whose size is proportional to the number +of 4KB pages contained in the UMEM. That allocation might fail when +the number of buffers allocated by VPP is too high. That number can be +controlled with the `buffers { buffers-per-numa }` configuration option. +Finally, note that because of this limitation, this plugin is unlikely +to be compatible with the use of 1GB hugepages. ## Requirements The Linux kernel interface must be up and have enough queues before @@ -29,9 +36,10 @@ AF_XDP interface, and only them. Depending on your configuration, there will usually be several RX queues (typically 1 per core) and packets are spread accross queues by RSS. In order to receive consistent traffic, you **must** program the NIC dispatching accordingly. The simplest way -to get all the packets is to reconfigure the Linux kernel driver to use -only `num_rx_queues` RX queues (ie all NIC queues will be associated -with the AF_XDP socket): +to get all the packets is to specify `num-rx-queues all` to grab all +available queues or to reconfigure the Linux kernel driver to use only +`num_rx_queues` RX queues (ie all NIC queues will be associated with +the AF_XDP socket): ``` ~# ethtool -L combined ``` @@ -55,25 +63,21 @@ kernel interface in promiscuous mode: ## Security considerations When creating an AF_XDP interface, it will receive all packets arriving -to the NIC RX queue #0. You need to configure the Linux kernel NIC -driver properly to ensure that only intented packets will arrive in -this queue. There is no way to filter the packets after-the-fact using -eg. netfilter or eBPF. +to the NIC RX queue [0, num_rx_queues[`. You need to configure the Linux +kernel NIC driver properly to ensure that only intented packets will +arrive in this queue. There is no way to filter the packets after-the-fact +using eg. netfilter or eBPF. ## Quickstart -1. Setup the Linux kernel interface (enp216s0f0 here) to use 4 queues: -``` -~# ethtool -L enp216s0f0 combined 4 -``` -2. Put the Linux kernel interface up and in promiscuous mode: +1. Put the Linux kernel interface up and in promiscuous mode: ``` ~# ip l set dev enp216s0f0 promisc on up ``` -3. Create the AF_XDP interface: +2. Create the AF_XDP interface: ``` -~# vppctl create int af_xdp host-if enp216s0f0 num-rx-queues 4 +~# vppctl create int af_xdp host-if enp216s0f0 num-rx-queues all ``` -4. Use the interface as usual, eg.: +3. Use the interface as usual, eg.: ``` ~# vppctl set int ip addr enp216s0f0/0 1.1.1.1/24 ~# vppctl set int st enp216s0f0/0 up diff --git a/src/plugins/af_xdp/cli.c b/src/plugins/af_xdp/cli.c index 5fe7c2ef399b..d5f21d4c3919 100644 --- a/src/plugins/af_xdp/cli.c +++ b/src/plugins/af_xdp/cli.c @@ -47,7 +47,7 @@ af_xdp_create_command_fn (vlib_main_t * vm, unformat_input_t * input, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (af_xdp_create_command, static) = { .path = "create interface af_xdp", - .short_help = "create interface af_xdp [name ifname] [rx-queue-size size] [tx-queue-size size] [num-rx-queues num] [prog pathname] [zero-copy|no-zero-copy]", + .short_help = "create interface af_xdp [name ifname] [rx-queue-size size] [tx-queue-size size] [num-rx-queues ] [prog pathname] [zero-copy|no-zero-copy]", .function = af_xdp_create_command_fn, }; /* *INDENT-ON* */ diff --git a/src/plugins/af_xdp/device.c b/src/plugins/af_xdp/device.c index 9bca41c962c1..5090d3a649a1 100644 --- a/src/plugins/af_xdp/device.c +++ b/src/plugins/af_xdp/device.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "af_xdp.h" @@ -171,14 +172,27 @@ static int af_xdp_create_queue (vlib_main_t * vm, af_xdp_create_if_args_t * args, af_xdp_device_t * ad, int qid, int rxq_num, int txq_num) { - struct xsk_umem **umem = vec_elt_at_index (ad->umem, qid); - struct xsk_socket **xsk = vec_elt_at_index (ad->xsk, qid); - af_xdp_rxq_t *rxq = vec_elt_at_index (ad->rxqs, qid); - af_xdp_txq_t *txq = vec_elt_at_index (ad->txqs, qid); + struct xsk_umem **umem; + struct xsk_socket **xsk; + af_xdp_rxq_t *rxq; + af_xdp_txq_t *txq; struct xsk_umem_config umem_config; struct xsk_socket_config sock_config; struct xdp_options opt; socklen_t optlen; + + vec_validate_aligned (ad->umem, qid, CLIB_CACHE_LINE_BYTES); + umem = vec_elt_at_index (ad->umem, qid); + + vec_validate_aligned (ad->xsk, qid, CLIB_CACHE_LINE_BYTES); + xsk = vec_elt_at_index (ad->xsk, qid); + + vec_validate_aligned (ad->rxqs, qid, CLIB_CACHE_LINE_BYTES); + rxq = vec_elt_at_index (ad->rxqs, qid); + + vec_validate_aligned (ad->txqs, qid, CLIB_CACHE_LINE_BYTES); + txq = vec_elt_at_index (ad->txqs, qid); + /* * fq and cq must always be allocated even if unused * whereas rx and tx indicates whether we want rxq, txq, or both @@ -260,21 +274,18 @@ af_xdp_create_queue (vlib_main_t * vm, af_xdp_create_if_args_t * args, static int af_xdp_get_numa (const char *ifname) { - FILE *fptr; + char *path; + clib_error_t *err; int numa; - char *s; - - s = (char *) format (0, "/sys/class/net/%s/device/numa_node%c", ifname, 0); - fptr = fopen (s, "rb"); - vec_free (s); - - if (!fptr) - return 0; - if (fscanf (fptr, "%d\n", &numa) != 1) + path = + (char *) format (0, "/sys/class/net/%s/device/numa_node%c", ifname, 0); + err = clib_sysfs_read (path, "%d", &numa); + if (err || numa < 0) numa = 0; - fclose (fptr); + clib_error_free (err); + vec_free (path); return numa; } @@ -335,10 +346,6 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args) goto err1; q_num = clib_max (rxq_num, txq_num); - vec_validate_aligned (ad->rxqs, q_num - 1, CLIB_CACHE_LINE_BYTES); - vec_validate_aligned (ad->txqs, q_num - 1, CLIB_CACHE_LINE_BYTES); - vec_validate_aligned (ad->umem, q_num - 1, CLIB_CACHE_LINE_BYTES); - vec_validate_aligned (ad->xsk, q_num - 1, CLIB_CACHE_LINE_BYTES); ad->txq_num = txq_num; for (i = 0; i < q_num; i++) { @@ -347,10 +354,10 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args) /* * queue creation failed * it is only a fatal error if we could not create the number of rx - * queues requested explicitely by the user + * queues requested explicitely by the user and the user did not + * requested 'max' * we might create less tx queues than workers but this is ok */ - af_xdp_txq_t *txq; /* fixup vectors length */ vec_set_len (ad->umem, i); @@ -358,15 +365,17 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args) vec_set_len (ad->rxqs, i); vec_set_len (ad->txqs, i); - if (i < rxq_num) + if (i < rxq_num && AF_XDP_NUM_RX_QUEUES_ALL != rxq_num) goto err1; /* failed creating requested rxq: fatal error, bailing out */ - /* - * we created all rxq but failed some txq: not an error but - * initialize lock for shared txq - */ - ad->txq_num = i; - vec_foreach (txq, ad->txqs) clib_spinlock_init (&txq->lock); + if (i < txq_num) + { + /* we created less txq than threads not an error but initialize lock for shared txq */ + af_xdp_txq_t *txq; + ad->txq_num = i; + vec_foreach (txq, ad->txqs) clib_spinlock_init (&txq->lock); + } + args->rv = 0; clib_error_free (args->error); break; @@ -406,7 +415,7 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args) vnet_hw_interface_set_input_node (vnm, ad->hw_if_index, af_xdp_input_node.index); - for (i = 0; i < rxq_num; i++) + for (i = 0; i < vec_len (ad->rxqs); i++) { af_xdp_rxq_t *rxq = vec_elt_at_index (ad->rxqs, i); clib_file_t f = { diff --git a/src/plugins/af_xdp/unformat.c b/src/plugins/af_xdp/unformat.c index 154d459900e4..b22924641121 100644 --- a/src/plugins/af_xdp/unformat.c +++ b/src/plugins/af_xdp/unformat.c @@ -40,6 +40,8 @@ unformat_af_xdp_create_if_args (unformat_input_t * input, va_list * vargs) ; else if (unformat (line_input, "tx-queue-size %u", &args->txq_size)) ; + else if (unformat (line_input, "num-rx-queues all")) + args->rxq_num = AF_XDP_NUM_RX_QUEUES_ALL; else if (unformat (line_input, "num-rx-queues %u", &args->rxq_num)) ; else if (unformat (line_input, "prog %s", &args->prog)) diff --git a/src/plugins/avf/avf.h b/src/plugins/avf/avf.h index 5d5178260097..43741dcad835 100644 --- a/src/plugins/avf/avf.h +++ b/src/plugins/avf/avf.h @@ -200,8 +200,10 @@ typedef struct typedef enum { AVF_PROCESS_EVENT_START = 1, - AVF_PROCESS_EVENT_STOP = 2, + AVF_PROCESS_EVENT_DELETE_IF = 2, AVF_PROCESS_EVENT_AQ_INT = 3, + AVF_PROCESS_EVENT_SET_PROMISC_ENABLE = 4, + AVF_PROCESS_EVENT_SET_PROMISC_DISABLE = 5, } avf_process_event_t; typedef struct @@ -223,7 +225,7 @@ typedef struct { u16 msg_id_base; - avf_device_t *devices; + avf_device_t **devices; avf_per_thread_data_t *per_thread_data; vlib_log_class_t log_class; @@ -246,9 +248,9 @@ typedef struct } avf_create_if_args_t; void avf_create_if (vlib_main_t * vm, avf_create_if_args_t * args); -void avf_delete_if (vlib_main_t * vm, avf_device_t * ad); extern vlib_node_registration_t avf_input_node; +extern vlib_node_registration_t avf_process_node; extern vnet_device_class_t avf_device_class; /* format.c */ @@ -256,6 +258,12 @@ format_function_t format_avf_device; format_function_t format_avf_device_name; format_function_t format_avf_input_trace; +static_always_inline avf_device_t * +avf_get_device (u32 dev_instance) +{ + return pool_elt_at_index (avf_main.devices, dev_instance)[0]; +} + static inline u32 avf_get_u32 (void *start, int offset) { diff --git a/src/plugins/avf/avf_api.c b/src/plugins/avf/avf_api.c index 1ddc45f23484..504fa31eb3de 100644 --- a/src/plugins/avf/avf_api.c +++ b/src/plugins/avf/avf_api.c @@ -66,7 +66,6 @@ vl_api_avf_delete_t_handler (vl_api_avf_delete_t * mp) vnet_main_t *vnm = vnet_get_main (); avf_main_t *am = &avf_main; vl_api_avf_delete_reply_t *rmp; - avf_device_t *ad; vnet_hw_interface_t *hw; int rv = 0; @@ -79,9 +78,8 @@ vl_api_avf_delete_t_handler (vl_api_avf_delete_t * mp) goto reply; } - ad = pool_elt_at_index (am->devices, hw->dev_instance); - - avf_delete_if (vm, ad); + vlib_process_signal_event (vm, avf_process_node.index, + AVF_PROCESS_EVENT_DELETE_IF, hw->dev_instance); reply: REPLY_MACRO (VL_API_AVF_DELETE_REPLY + am->msg_id_base); @@ -93,6 +91,9 @@ static clib_error_t * avf_plugin_api_hookup (vlib_main_t * vm) { avf_main_t *avm = &avf_main; + api_main_t *am = vlibapi_get_main (); + + am->is_mp_safe[VL_API_AVF_DELETE] = 1; /* ask for a correctly-sized block of API message decode slots */ avm->msg_id_base = setup_message_id_table (); diff --git a/src/plugins/avf/cli.c b/src/plugins/avf/cli.c index 414163ac8a25..32c19f46590a 100644 --- a/src/plugins/avf/cli.c +++ b/src/plugins/avf/cli.c @@ -84,8 +84,6 @@ avf_delete_command_fn (vlib_main_t * vm, unformat_input_t * input, unformat_input_t _line_input, *line_input = &_line_input; u32 sw_if_index = ~0; vnet_hw_interface_t *hw; - avf_main_t *am = &avf_main; - avf_device_t *ad; vnet_main_t *vnm = vnet_get_main (); /* Get a line of input. */ @@ -113,9 +111,8 @@ avf_delete_command_fn (vlib_main_t * vm, unformat_input_t * input, if (hw == NULL || avf_device_class.index != hw->dev_class_index) return clib_error_return (0, "not an AVF interface"); - ad = pool_elt_at_index (am->devices, hw->dev_instance); - - avf_delete_if (vm, ad); + vlib_process_signal_event (vm, avf_process_node.index, + AVF_PROCESS_EVENT_DELETE_IF, hw->dev_instance); return 0; } @@ -126,6 +123,7 @@ VLIB_CLI_COMMAND (avf_delete_command, static) = { .short_help = "delete interface avf " "{ | sw_if_index }", .function = avf_delete_command_fn, + .is_mp_safe = 1, }; /* *INDENT-ON* */ @@ -136,7 +134,6 @@ avf_test_command_fn (vlib_main_t * vm, unformat_input_t * input, unformat_input_t _line_input, *line_input = &_line_input; u32 sw_if_index = ~0; vnet_hw_interface_t *hw; - avf_main_t *am = &avf_main; avf_device_t *ad; vnet_main_t *vnm = vnet_get_main (); int test_irq = 0, enable_elog = 0, disable_elog = 0; @@ -172,7 +169,7 @@ avf_test_command_fn (vlib_main_t * vm, unformat_input_t * input, if (hw == NULL || avf_device_class.index != hw->dev_class_index) return clib_error_return (0, "not a AVF interface"); - ad = pool_elt_at_index (am->devices, hw->dev_instance); + ad = avf_get_device (hw->dev_instance); if (enable_elog) ad->flags |= AVF_DEVICE_F_ELOG; diff --git a/src/plugins/avf/device.c b/src/plugins/avf/device.c index f086cd67741f..0481e61b2949 100644 --- a/src/plugins/avf/device.c +++ b/src/plugins/avf/device.c @@ -35,6 +35,7 @@ #define PCI_DEVICE_ID_INTEL_X722_VF 0x37cd avf_main_t avf_main; +void avf_delete_if (vlib_main_t * vm, avf_device_t * ad, int with_barrier); static pci_device_id_t avf_pci_device_ids[] = { {.vendor_id = PCI_VENDOR_ID_INTEL,.device_id = PCI_DEVICE_ID_INTEL_AVF}, @@ -398,6 +399,11 @@ avf_send_to_pf (vlib_main_t * vm, avf_device_t * ad, virtchnl_ops_t op, u32 head; f64 t0, suspend_time = AVF_SEND_TO_PF_SUSPEND_TIME; + /* adminq operations should be only done from process node after device + * is initialized */ + ASSERT ((ad->flags & AVF_DEVICE_F_INITIALIZED) == 0 || + vlib_get_current_process_node_index (vm) == avf_process_node.index); + /* suppress interrupt in the next adminq receive slot as we are going to wait for response we only need interrupts when event is received */ @@ -1156,9 +1162,7 @@ static u32 avf_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hw, u32 flags) { vlib_main_t *vm = vlib_get_main (); - avf_main_t *am = &avf_main; - avf_device_t *ad = vec_elt_at_index (am->devices, hw->dev_instance); - clib_error_t *error; + avf_device_t *ad = avf_get_device (hw->dev_instance); u8 promisc_enabled; switch (flags) @@ -1174,13 +1178,12 @@ avf_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hw, u32 flags) } promisc_enabled = ((ad->flags & AVF_DEVICE_F_PROMISC) != 0); - if ((error = avf_config_promisc_mode (vm, ad, promisc_enabled))) - { - avf_log_err (ad, "%s: %U", format_clib_error, error); - clib_error_free (error); - return ~0; - } + vlib_process_signal_event (vm, avf_process_node.index, + promisc_enabled ? + AVF_PROCESS_EVENT_SET_PROMISC_ENABLE : + AVF_PROCESS_EVENT_SET_PROMISC_DISABLE, + hw->dev_instance); return 0; } @@ -1188,11 +1191,12 @@ static uword avf_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { avf_main_t *am = &avf_main; - avf_device_t *ad; uword *event_data = 0, event_type; int enabled = 0, irq; f64 last_run_duration = 0; f64 last_periodic_time = 0; + avf_device_t **dev_pointers = 0; + u32 i; while (1) { @@ -1202,7 +1206,6 @@ avf_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) vlib_process_wait_for_event (vm); event_type = vlib_process_get_events (vm, &event_data); - vec_reset_length (event_data); irq = 0; switch (event_type) @@ -1213,21 +1216,59 @@ avf_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) case AVF_PROCESS_EVENT_START: enabled = 1; break; - case AVF_PROCESS_EVENT_STOP: - enabled = 0; - continue; + case AVF_PROCESS_EVENT_DELETE_IF: + for (int i = 0; i < vec_len (event_data); i++) + { + avf_device_t *ad = avf_get_device (event_data[i]); + avf_delete_if (vm, ad, /* with_barrier */ 1); + } + if (pool_elts (am->devices) < 1) + enabled = 0; + break; case AVF_PROCESS_EVENT_AQ_INT: irq = 1; break; + case AVF_PROCESS_EVENT_SET_PROMISC_ENABLE: + case AVF_PROCESS_EVENT_SET_PROMISC_DISABLE: + for (int i = 0; i < vec_len (event_data); i++) + { + avf_device_t *ad = avf_get_device (event_data[i]); + clib_error_t *err; + int is_enable = 0; + + if (event_type == AVF_PROCESS_EVENT_SET_PROMISC_ENABLE) + is_enable = 1; + + if ((err = avf_config_promisc_mode (vm, ad, is_enable))) + { + avf_log_err (ad, "error: %U", format_clib_error, err); + clib_error_free (err); + } + } + break; + default: ASSERT (0); } + vec_reset_length (event_data); + + if (enabled == 0) + continue; + + /* create local list of device pointers as device pool may grow + * during suspend */ + vec_reset_length (dev_pointers); /* *INDENT-OFF* */ - pool_foreach (ad, am->devices, + pool_foreach_index (i, am->devices, + { + vec_add1 (dev_pointers, avf_get_device (i)); + }); + + vec_foreach_index (i, dev_pointers) { - avf_process_one_device (vm, ad, irq); - }); + avf_process_one_device (vm, dev_pointers[i], irq); + }; /* *INDENT-ON* */ last_run_duration = vlib_time_now (vm) - last_periodic_time; } @@ -1235,7 +1276,7 @@ avf_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) } /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (avf_process_node, static) = { +VLIB_REGISTER_NODE (avf_process_node) = { .function = avf_process, .type = VLIB_NODE_TYPE_PROCESS, .name = "avf-process", @@ -1245,9 +1286,8 @@ VLIB_REGISTER_NODE (avf_process_node, static) = { static void avf_irq_0_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h, u16 line) { - avf_main_t *am = &avf_main; uword pd = vlib_pci_get_private_data (vm, h); - avf_device_t *ad = pool_elt_at_index (am->devices, pd); + avf_device_t *ad = avf_get_device (pd); u32 icr0; icr0 = avf_reg_read (ad, AVFINT_ICR0); @@ -1284,9 +1324,8 @@ static void avf_irq_n_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h, u16 line) { vnet_main_t *vnm = vnet_get_main (); - avf_main_t *am = &avf_main; uword pd = vlib_pci_get_private_data (vm, h); - avf_device_t *ad = pool_elt_at_index (am->devices, pd); + avf_device_t *ad = avf_get_device (pd); if (ad->flags & AVF_DEVICE_F_ELOG) { @@ -1316,17 +1355,23 @@ avf_irq_n_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h, u16 line) } void -avf_delete_if (vlib_main_t * vm, avf_device_t * ad) +avf_delete_if (vlib_main_t * vm, avf_device_t * ad, int with_barrier) { vnet_main_t *vnm = vnet_get_main (); avf_main_t *am = &avf_main; int i; + ad->flags &= ~AVF_DEVICE_F_ADMIN_UP; + if (ad->hw_if_index) { + if (with_barrier) + vlib_worker_thread_barrier_sync (vm); vnet_hw_interface_set_flags (vnm, ad->hw_if_index, 0); vnet_hw_interface_unassign_rx_thread (vnm, ad->hw_if_index, 0); ethernet_delete_interface (vnm, ad->hw_if_index); + if (with_barrier) + vlib_worker_thread_barrier_release (vm); } vlib_pci_device_close (vm, ad->pci_dev_handle); @@ -1369,7 +1414,8 @@ avf_delete_if (vlib_main_t * vm, avf_device_t * ad) clib_error_free (ad->error); clib_memset (ad, 0, sizeof (*ad)); - pool_put (am->devices, ad); + pool_put_index (am->devices, ad->dev_instance); + clib_mem_free (ad); } void @@ -1377,7 +1423,7 @@ avf_create_if (vlib_main_t * vm, avf_create_if_args_t * args) { vnet_main_t *vnm = vnet_get_main (); avf_main_t *am = &avf_main; - avf_device_t *ad; + avf_device_t *ad, **adp; vlib_pci_dev_handle_t h; clib_error_t *error = 0; int i; @@ -1395,8 +1441,11 @@ avf_create_if (vlib_main_t * vm, avf_create_if_args_t * args) return; } - pool_get (am->devices, ad); - ad->dev_instance = ad - am->devices; + pool_get (am->devices, adp); + adp[0] = ad = clib_mem_alloc_aligned (sizeof (avf_device_t), + CLIB_CACHE_LINE_BYTES); + clib_memset (ad, 0, sizeof (avf_device_t)); + ad->dev_instance = adp - am->devices; ad->per_interface_next_index = ~0; ad->name = vec_dup (args->name); @@ -1406,7 +1455,8 @@ avf_create_if (vlib_main_t * vm, avf_create_if_args_t * args) if ((error = vlib_pci_device_open (vm, &args->addr, avf_pci_device_ids, &h))) { - pool_put (am->devices, ad); + pool_put (am->devices, adp); + clib_mem_free (ad); args->rv = VNET_API_ERROR_INVALID_INTERFACE; args->error = clib_error_return (error, "pci-addr %U", format_vlib_pci_addr, @@ -1530,7 +1580,7 @@ avf_create_if (vlib_main_t * vm, avf_create_if_args_t * args) return; error: - avf_delete_if (vm, ad); + avf_delete_if (vm, ad, /* with_barrier */ 0); args->rv = VNET_API_ERROR_INVALID_INTERFACE; args->error = clib_error_return (error, "pci-addr %U", format_vlib_pci_addr, &args->addr); @@ -1541,8 +1591,7 @@ static clib_error_t * avf_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); - avf_main_t *am = &avf_main; - avf_device_t *ad = vec_elt_at_index (am->devices, hi->dev_instance); + avf_device_t *ad = avf_get_device (hi->dev_instance); uword is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0; if (ad->flags & AVF_DEVICE_F_ERROR) @@ -1566,9 +1615,8 @@ static clib_error_t * avf_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid, vnet_hw_interface_rx_mode mode) { - avf_main_t *am = &avf_main; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); - avf_device_t *ad = pool_elt_at_index (am->devices, hw->dev_instance); + avf_device_t *ad = avf_get_device (hw->dev_instance); avf_rxq_t *rxq = vec_elt_at_index (ad->rxqs, qid); if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) @@ -1598,9 +1646,8 @@ static void avf_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index, u32 node_index) { - avf_main_t *am = &avf_main; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); - avf_device_t *ad = pool_elt_at_index (am->devices, hw->dev_instance); + avf_device_t *ad = avf_get_device (hw->dev_instance); /* Shut off redirection */ if (node_index == ~0) @@ -1622,8 +1669,7 @@ static char *avf_tx_func_error_strings[] = { static void avf_clear_hw_interface_counters (u32 instance) { - avf_main_t *am = &avf_main; - avf_device_t *ad = vec_elt_at_index (am->devices, instance); + avf_device_t *ad = avf_get_device (instance); clib_memcpy_fast (&ad->last_cleared_eth_stats, &ad->eth_stats, sizeof (ad->eth_stats)); } diff --git a/src/plugins/avf/format.c b/src/plugins/avf/format.c index bc2b94ecc46c..e5da0e2bbf60 100644 --- a/src/plugins/avf/format.c +++ b/src/plugins/avf/format.c @@ -27,8 +27,7 @@ format_avf_device_name (u8 * s, va_list * args) { vlib_main_t *vm = vlib_get_main (); u32 i = va_arg (*args, u32); - avf_main_t *am = &avf_main; - avf_device_t *ad = vec_elt_at_index (am->devices, i); + avf_device_t *ad = avf_get_device (i); vlib_pci_addr_t *addr = vlib_pci_get_addr (vm, ad->pci_dev_handle); if (ad->name) @@ -88,8 +87,7 @@ u8 * format_avf_device (u8 * s, va_list * args) { u32 i = va_arg (*args, u32); - avf_main_t *am = &avf_main; - avf_device_t *ad = vec_elt_at_index (am->devices, i); + avf_device_t *ad = avf_get_device (i); u32 indent = format_get_indent (s); u8 *a = 0; diff --git a/src/plugins/avf/input.c b/src/plugins/avf/input.c index da5556a391e1..0ccf7721835c 100644 --- a/src/plugins/avf/input.c +++ b/src/plugins/avf/input.c @@ -444,14 +444,13 @@ VLIB_NODE_FN (avf_input_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { u32 n_rx = 0; - avf_main_t *am = &avf_main; vnet_device_input_runtime_t *rt = (void *) node->runtime_data; vnet_device_and_queue_t *dq; foreach_device_and_queue (dq, rt->devices_and_queues) { avf_device_t *ad; - ad = vec_elt_at_index (am->devices, dq->dev_instance); + ad = avf_get_device (dq->dev_instance); if ((ad->flags & AVF_DEVICE_F_ADMIN_UP) == 0) continue; n_rx += avf_device_input_inline (vm, node, frame, ad, dq->queue_id); diff --git a/src/plugins/avf/output.c b/src/plugins/avf/output.c index 6c43885569e6..e5b53ba54577 100644 --- a/src/plugins/avf/output.c +++ b/src/plugins/avf/output.c @@ -267,9 +267,8 @@ VNET_DEVICE_CLASS_TX_FN (avf_device_class) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - avf_main_t *am = &avf_main; vnet_interface_output_runtime_t *rd = (void *) node->runtime_data; - avf_device_t *ad = pool_elt_at_index (am->devices, rd->dev_instance); + avf_device_t *ad = avf_get_device (rd->dev_instance); u32 thread_index = vm->thread_index; u8 qid = thread_index; avf_txq_t *txq = vec_elt_at_index (ad->txqs, qid % ad->num_queue_pairs); diff --git a/src/plugins/crypto_native/aes_gcm.c b/src/plugins/crypto_native/aes_gcm.c index 9aeed9dee2e3..e0c1e6c12c3e 100644 --- a/src/plugins/crypto_native/aes_gcm.c +++ b/src/plugins/crypto_native/aes_gcm.c @@ -63,6 +63,7 @@ typedef enum static const u32x4 ctr_inv_1 = { 0, 0, 0, 1 << 24 }; +#ifndef __VAES__ static_always_inline void aes_gcm_enc_first_round (u8x16 * r, aes_gcm_counter_t * ctr, u8x16 k, int n_blocks) @@ -106,6 +107,7 @@ aes_gcm_enc_last_round (u8x16 * r, u8x16 * d, u8x16 const *k, for (int i = 0; i < n_blocks; i++) d[i] ^= aes_enc_last_round (r[i], k[rounds]); } +#endif static_always_inline u8x16 aes_gcm_ghash_blocks (u8x16 T, aes_gcm_key_data_t * kd, @@ -161,6 +163,7 @@ aes_gcm_ghash (u8x16 T, aes_gcm_key_data_t * kd, u8x16u * in, u32 n_left) return T; } +#ifndef __VAES__ static_always_inline u8x16 aes_gcm_calc (u8x16 T, aes_gcm_key_data_t * kd, u8x16 * d, aes_gcm_counter_t * ctr, u8x16u * inv, u8x16u * outv, @@ -414,6 +417,7 @@ aes_gcm_ghash_last (u8x16 T, aes_gcm_key_data_t * kd, u8x16 * d, ghash_reduce2 (gd); return ghash_final (gd); } +#endif #ifdef __VAES__ static const u32x16 ctr_inv_1234 = { @@ -748,7 +752,6 @@ static_always_inline u8x16 aes_gcm_enc (u8x16 T, aes_gcm_key_data_t * kd, aes_gcm_counter_t * ctr, u8x16u * inv, u8x16u * outv, u32 n_left, int rounds) { - u8x16 d[4]; aes_gcm_flags_t f = AES_GCM_F_ENCRYPT; if (n_left == 0) @@ -841,8 +844,8 @@ aes_gcm_enc (u8x16 T, aes_gcm_key_data_t * kd, aes_gcm_counter_t * ctr, T = aes4_gcm_calc (T, kd, d4, ctr, inv, outv, rounds, 1, n_left, f); return aes4_gcm_ghash_last (T, kd, d4, 1, n_left); -#endif - +#else + u8x16 d[4]; if (n_left < 64) { f |= AES_GCM_F_LAST_ROUND; @@ -928,6 +931,7 @@ aes_gcm_enc (u8x16 T, aes_gcm_key_data_t * kd, aes_gcm_counter_t * ctr, T = aes_gcm_calc (T, kd, d, ctr, inv, outv, rounds, 1, n_left, f); return aes_gcm_ghash_last (T, kd, d, 1, n_left); +#endif } static_always_inline u8x16 diff --git a/src/plugins/crypto_native/crypto_native.h b/src/plugins/crypto_native/crypto_native.h index f1153737deae..d5c33daa1a63 100644 --- a/src/plugins/crypto_native/crypto_native.h +++ b/src/plugins/crypto_native/crypto_native.h @@ -23,7 +23,7 @@ typedef void *(crypto_native_key_fn_t) (vnet_crypto_key_t * key); typedef struct { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - u8x16 cbc_iv[4]; + u8x16 cbc_iv[16]; } crypto_native_per_thread_data_t; typedef struct diff --git a/src/plugins/crypto_sw_scheduler/main.c b/src/plugins/crypto_sw_scheduler/main.c index a450bc16e8e8..7de84ff12001 100644 --- a/src/plugins/crypto_sw_scheduler/main.c +++ b/src/plugins/crypto_sw_scheduler/main.c @@ -543,7 +543,7 @@ sw_scheduler_show_workers (vlib_main_t * vm, unformat_input_t * input, u32 i; vlib_cli_output (vm, "%-7s%-20s%-8s", "ID", "Name", "Crypto"); - for (i = vlib_num_workers () >= 0; i < vlib_thread_main.n_vlib_mains; i++) + for (i = 1; i < vlib_thread_main.n_vlib_mains; i++) { vlib_cli_output (vm, "%-7d%-20s%-8s", vlib_get_worker_index (i), (vlib_worker_threads + i)->name, diff --git a/src/plugins/dns/dns.c b/src/plugins/dns/dns.c index bdf14961a783..bb60f748a41f 100644 --- a/src/plugins/dns/dns.c +++ b/src/plugins/dns/dns.c @@ -68,14 +68,13 @@ dns_cache_clear (dns_main_t * dm) } static int -dns_enable_disable (dns_main_t * dm, int is_enable) +dns_enable_disable (vlib_main_t * vm, dns_main_t * dm, int is_enable) { vlib_thread_main_t *tm = &vlib_thread_main; u32 n_vlib_mains = tm->n_vlib_mains; - vlib_main_t *vm = dm->vlib_main; /* Create the resolver process if not done already */ - vnet_dns_create_resolver_process (dm); + vnet_dns_create_resolver_process (vm, dm); if (is_enable) { @@ -122,10 +121,11 @@ static void vl_api_dns_enable_disable_t_handler (vl_api_dns_enable_disable_t * mp) { vl_api_dns_enable_disable_reply_t *rmp; + vlib_main_t *vm = vlib_get_main (); dns_main_t *dm = &dns_main; int rv; - rv = dns_enable_disable (dm, mp->enable); + rv = dns_enable_disable (vm, dm, mp->enable); REPLY_MACRO (VL_API_DNS_ENABLE_DISABLE_REPLY); } @@ -218,10 +218,9 @@ static void vl_api_dns_name_server_add_del_t_handler } void -vnet_dns_send_dns4_request (dns_main_t * dm, +vnet_dns_send_dns4_request (vlib_main_t * vm, dns_main_t * dm, dns_cache_entry_t * ep, ip4_address_t * server) { - vlib_main_t *vm = dm->vlib_main; f64 now = vlib_time_now (vm); u32 bi; vlib_buffer_t *b; @@ -288,7 +287,7 @@ vnet_dns_send_dns4_request (dns_main_t * dm, found_src_address: /* Go get a buffer */ - if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1) + if (vlib_buffer_alloc (vm, &bi, 1) != 1) return; b = vlib_get_buffer (vm, bi); @@ -337,10 +336,9 @@ vnet_dns_send_dns4_request (dns_main_t * dm, } void -vnet_dns_send_dns6_request (dns_main_t * dm, +vnet_dns_send_dns6_request (vlib_main_t * vm, dns_main_t * dm, dns_cache_entry_t * ep, ip6_address_t * server) { - vlib_main_t *vm = dm->vlib_main; f64 now = vlib_time_now (vm); u32 bi; vlib_buffer_t *b; @@ -397,7 +395,7 @@ vnet_dns_send_dns6_request (dns_main_t * dm, found_src_address: /* Go get a buffer */ - if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1) + if (vlib_buffer_alloc (vm, &bi, 1) != 1) return; b = vlib_get_buffer (vm, bi); @@ -536,7 +534,8 @@ vnet_dns_labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here) } void -vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep) +vnet_send_dns_request (vlib_main_t * vm, dns_main_t * dm, + dns_cache_entry_t * ep) { dns_header_t *h; dns_query_t *qp; @@ -610,7 +609,7 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep) if (vec_len (dm->ip6_name_servers)) { vnet_dns_send_dns6_request - (dm, ep, dm->ip6_name_servers + ep->server_rotor); + (vm, dm, ep, dm->ip6_name_servers + ep->server_rotor); goto out; } else @@ -619,7 +618,7 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep) if (vec_len (dm->ip4_name_servers)) { vnet_dns_send_dns4_request - (dm, ep, dm->ip4_name_servers + ep->server_rotor); + (vm, dm, ep, dm->ip4_name_servers + ep->server_rotor); goto out; } } @@ -647,14 +646,14 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep) if (ep->server_af == 1 /* ip6 */ ) vnet_dns_send_dns6_request - (dm, ep, dm->ip6_name_servers + ep->server_rotor); + (vm, dm, ep, dm->ip6_name_servers + ep->server_rotor); else vnet_dns_send_dns4_request - (dm, ep, dm->ip4_name_servers + ep->server_rotor); + (vm, dm, ep, dm->ip4_name_servers + ep->server_rotor); out: - vlib_process_signal_event_mt (dm->vlib_main, + vlib_process_signal_event_mt (vm, dm->resolver_process_node_index, DNS_RESOLVER_EVENT_PENDING, 0); } @@ -808,8 +807,8 @@ dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data) } int -vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t, - dns_cache_entry_t ** retp) +vnet_dns_resolve_name (vlib_main_t * vm, dns_main_t * dm, u8 * name, + dns_pending_request_t * t, dns_cache_entry_t ** retp) { dns_cache_entry_t *ep; int rv; @@ -818,7 +817,7 @@ vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t, dns_pending_request_t *pr; int count; - now = vlib_time_now (dm->vlib_main); + now = vlib_time_now (vm); /* In case we can't actually answer the question right now... */ *retp = 0; @@ -950,7 +949,7 @@ vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t, clib_memcpy (pr->dst_address, t->dst_address, count); } - vnet_send_dns_request (dm, ep); + vnet_send_dns_request (vm, dm, ep); dns_cache_unlock (dm); return 0; } @@ -964,7 +963,8 @@ _(pending_requests) */ int -vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply) +vnet_dns_cname_indirection_nolock (vlib_main_t * vm, dns_main_t * dm, + u32 ep_index, u8 * reply) { dns_header_t *h; dns_query_t *qp; @@ -1074,7 +1074,7 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply) found_last_request: - now = vlib_time_now (dm->vlib_main); + now = vlib_time_now (vm); cname = vnet_dns_labels_to_name (rr->rdata, reply, &pos2); /* Save the cname */ vec_add1 (cname, 0); @@ -1154,7 +1154,7 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply) */ vec_add1 (dm->unresolved_entries, next_ep - dm->entries); - vnet_send_dns_request (dm, next_ep); + vnet_send_dns_request (vm, dm, next_ep); return (1); } @@ -1438,6 +1438,7 @@ vnet_dns_response_to_name (u8 * response, static void vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp) { + vlib_main_t *vm = vlib_get_main (); dns_main_t *dm = &dns_main; vl_api_dns_resolve_name_reply_t *rmp; dns_cache_entry_t *ep; @@ -1451,7 +1452,7 @@ vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp) t0->client_index = mp->client_index; t0->client_context = mp->context; - rv = vnet_dns_resolve_name (dm, mp->name, t0, &ep); + rv = vnet_dns_resolve_name (vm, dm, mp->name, t0, &ep); /* Error, e.g. not enabled? Tell the user */ if (rv < 0) @@ -1471,17 +1472,12 @@ vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp) rmp->retval = clib_host_to_net_u32 (rv); })); /* *INDENT-ON* */ - - /* - * dns_resolve_name leaves the cache locked when it returns - * a cached result, so unlock it here. - */ - dns_cache_unlock (dm); } static void vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp) { + vlib_main_t *vm = vlib_get_main (); dns_main_t *dm = &dns_main; vl_api_dns_resolve_ip_reply_t *rmp; dns_cache_entry_t *ep; @@ -1529,7 +1525,7 @@ vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp) t0->client_index = mp->client_index; t0->client_context = mp->context; - rv = vnet_dns_resolve_name (dm, lookup_name, t0, &ep); + rv = vnet_dns_resolve_name (vm, dm, lookup_name, t0, &ep); vec_free (lookup_name); @@ -1551,12 +1547,6 @@ vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp) rmp->retval = clib_host_to_net_u32 (rv); })); /* *INDENT-ON* */ - - /* - * vnet_dns_resolve_name leaves the cache locked when it returns - * a cached result, so unlock it here. - */ - dns_cache_unlock (dm); } static clib_error_t * @@ -2728,18 +2718,19 @@ VLIB_CLI_COMMAND (test_dns_expire_command) = #endif void -vnet_send_dns6_reply (dns_main_t * dm, dns_pending_request_t * pr, - dns_cache_entry_t * ep, vlib_buffer_t * b0) +vnet_send_dns6_reply (vlib_main_t * vm, dns_main_t * dm, + dns_pending_request_t * pr, dns_cache_entry_t * ep, + vlib_buffer_t * b0) { clib_warning ("Unimplemented..."); } void -vnet_send_dns4_reply (dns_main_t * dm, dns_pending_request_t * pr, - dns_cache_entry_t * ep, vlib_buffer_t * b0) +vnet_send_dns4_reply (vlib_main_t * vm, dns_main_t * dm, + dns_pending_request_t * pr, dns_cache_entry_t * ep, + vlib_buffer_t * b0) { - vlib_main_t *vm = dm->vlib_main; u32 bi = 0; fib_prefix_t prefix; fib_node_index_t fei; @@ -3051,7 +3042,6 @@ dns_init (vlib_main_t * vm) { dns_main_t *dm = &dns_main; - dm->vlib_main = vm; dm->vnet_main = vnet_get_main (); dm->name_cache_size = 1000; dm->max_ttl_in_seconds = 86400; diff --git a/src/plugins/dns/dns.h b/src/plugins/dns/dns.h index 2351ab25f97c..e6ca440b48a5 100644 --- a/src/plugins/dns/dns.h +++ b/src/plugins/dns/dns.h @@ -123,7 +123,6 @@ typedef struct u16 msg_id_base; /* convenience */ - vlib_main_t *vlib_main; vnet_main_t *vnet_main; api_main_t *api_main; } dns_main_t; @@ -168,33 +167,36 @@ typedef enum DNS46_REPLY_N_ERROR, } dns46_reply_error_t; -void vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep); -int -vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply); +void vnet_send_dns_request (vlib_main_t * vm, dns_main_t * dm, + dns_cache_entry_t * ep); +int vnet_dns_cname_indirection_nolock (vlib_main_t * vm, dns_main_t * dm, + u32 ep_index, u8 * reply); int vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index); int -vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t, - dns_cache_entry_t ** retp); +vnet_dns_resolve_name (vlib_main_t * vm, dns_main_t * dm, u8 * name, + dns_pending_request_t * t, dns_cache_entry_t ** retp); void -vnet_dns_send_dns6_request (dns_main_t * dm, +vnet_dns_send_dns6_request (vlib_main_t * vm, dns_main_t * dm, dns_cache_entry_t * ep, ip6_address_t * server); void -vnet_dns_send_dns4_request (dns_main_t * dm, +vnet_dns_send_dns4_request (vlib_main_t * vm, dns_main_t * dm, dns_cache_entry_t * ep, ip4_address_t * server); -void vnet_send_dns4_reply (dns_main_t * dm, dns_pending_request_t * t, - dns_cache_entry_t * ep, vlib_buffer_t * b0); +void vnet_send_dns4_reply (vlib_main_t * vm, dns_main_t * dm, + dns_pending_request_t * t, dns_cache_entry_t * ep, + vlib_buffer_t * b0); -void vnet_send_dns6_reply (dns_main_t * dm, dns_pending_request_t * t, - dns_cache_entry_t * ep, vlib_buffer_t * b0); +void vnet_send_dns6_reply (vlib_main_t * vm, dns_main_t * dm, + dns_pending_request_t * t, dns_cache_entry_t * ep, + vlib_buffer_t * b0); u8 *vnet_dns_labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here); -void vnet_dns_create_resolver_process (dns_main_t * dm); +void vnet_dns_create_resolver_process (vlib_main_t * vm, dns_main_t * dm); format_function_t format_dns_reply; diff --git a/src/plugins/dns/request_node.c b/src/plugins/dns/request_node.c index d26e579cf472..a80876927e92 100644 --- a/src/plugins/dns/request_node.c +++ b/src/plugins/dns/request_node.c @@ -242,14 +242,14 @@ dns46_request_inline (vlib_main_t * vm, clib_memcpy_fast (t0->dst_address, ip40->src_address.as_u8, sizeof (ip4_address_t)); - vnet_dns_resolve_name (dm, name0, t0, &ep0); + vnet_dns_resolve_name (vm, dm, name0, t0, &ep0); if (ep0) { if (is_ip6) - vnet_send_dns6_reply (dm, t0, ep0, b0); + vnet_send_dns6_reply (vm, dm, t0, ep0, b0); else - vnet_send_dns4_reply (dm, t0, ep0, b0); + vnet_send_dns4_reply (vm, dm, t0, ep0, b0); next0 = DNS46_REQUEST_NEXT_IP_LOOKUP; } else diff --git a/src/plugins/dns/resolver_process.c b/src/plugins/dns/resolver_process.c index 71e5ba2ffabf..4aa101709c35 100644 --- a/src/plugins/dns/resolver_process.c +++ b/src/plugins/dns/resolver_process.c @@ -36,9 +36,8 @@ vnet_dns_response_to_name (u8 * response, u32 * min_ttlp); static void -resolve_event (dns_main_t * dm, f64 now, u8 * reply) +resolve_event (vlib_main_t * vm, dns_main_t * dm, f64 now, u8 * reply) { - vlib_main_t *vm = dm->vlib_main; dns_pending_request_t *pr; dns_header_t *d; u32 pool_index; @@ -76,7 +75,7 @@ resolve_event (dns_main_t * dm, f64 now, u8 * reply) vec_free (ep->dns_response); /* Handle [sic] recursion AKA CNAME indirection */ - rv = vnet_dns_cname_indirection_nolock (dm, pool_index, reply); + rv = vnet_dns_cname_indirection_nolock (vm, dm, pool_index, reply); /* CNAME found, further resolution pending, we're done here */ if (rv > 0) @@ -109,7 +108,7 @@ resolve_event (dns_main_t * dm, f64 now, u8 * reply) clib_warning ("Try server %U", format_ip6_address, dm->ip6_name_servers + ep->server_rotor); vnet_dns_send_dns6_request - (dm, ep, dm->ip6_name_servers + ep->server_rotor); + (vm, dm, ep, dm->ip6_name_servers + ep->server_rotor); } else { @@ -132,7 +131,7 @@ resolve_event (dns_main_t * dm, f64 now, u8 * reply) clib_warning ("Try server %U", format_ip4_address, dm->ip4_name_servers + ep->server_rotor); vnet_dns_send_dns4_request - (dm, ep, dm->ip4_name_servers + ep->server_rotor); + (vm, dm, ep, dm->ip4_name_servers + ep->server_rotor); } dns_cache_unlock (dm); return; @@ -222,9 +221,9 @@ resolve_event (dns_main_t * dm, f64 now, u8 * reply) case DNS_PEER_PENDING_IP_TO_NAME: case DNS_PEER_PENDING_NAME_TO_IP: if (pr->is_ip6) - vnet_send_dns6_reply (dm, pr, ep, 0 /* allocate a buffer */ ); + vnet_send_dns6_reply (vm, dm, pr, ep, 0 /* allocate a buffer */ ); else - vnet_send_dns4_reply (dm, pr, ep, 0 /* allocate a buffer */ ); + vnet_send_dns4_reply (vm, dm, pr, ep, 0 /* allocate a buffer */ ); break; default: clib_warning ("request type %d unknown", pr->request_type); @@ -286,7 +285,7 @@ resolve_event (dns_main_t * dm, f64 now, u8 * reply) } static void -retry_scan (dns_main_t * dm, f64 now) +retry_scan (vlib_main_t * vm, dns_main_t * dm, f64 now) { int i; dns_cache_entry_t *ep; @@ -297,7 +296,7 @@ retry_scan (dns_main_t * dm, f64 now) ep = pool_elt_at_index (dm->entries, dm->unresolved_entries[i]); ASSERT ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID) == 0); - vnet_send_dns_request (dm, ep); + vnet_send_dns_request (vm, dm, ep); dns_cache_unlock (dm); } } @@ -330,11 +329,11 @@ dns_resolver_process (vlib_main_t * vm, case DNS_RESOLVER_EVENT_RESOLVED: for (i = 0; i < vec_len (event_data); i++) - resolve_event (dm, now, (u8 *) event_data[i]); + resolve_event (vm, dm, now, (u8 *) event_data[i]); break; case ~0: /* timeout */ - retry_scan (dm, now); + retry_scan (vm, dm, now); break; } vec_reset_length (event_data); @@ -347,7 +346,7 @@ dns_resolver_process (vlib_main_t * vm, } void -vnet_dns_create_resolver_process (dns_main_t * dm) +vnet_dns_create_resolver_process (vlib_main_t * vm, dns_main_t * dm) { /* Already created the resolver process? */ if (dm->resolver_process_node_index > 0) @@ -355,7 +354,7 @@ vnet_dns_create_resolver_process (dns_main_t * dm) /* No, create it now and make a note of the node index */ dm->resolver_process_node_index = vlib_process_create - (dm->vlib_main, "dns-resolver-process", + (vm, "dns-resolver-process", dns_resolver_process, 16 /* log2_n_stack_bytes */ ); } diff --git a/src/plugins/dpdk/device/device.c b/src/plugins/dpdk/device/device.c index e58dd6f4228e..532987c31308 100644 --- a/src/plugins/dpdk/device/device.c +++ b/src/plugins/dpdk/device/device.c @@ -639,6 +639,13 @@ dpdk_interface_set_rss_queues (struct vnet_main_t *vnm, })); /* *INDENT-ON* */ + /* check valid_queue_count not zero, make coverity happy */ + if (valid_queue_count == 0) + { + err = clib_error_return (0, "must assign at least one valid rss queue"); + goto done; + } + valid_queue = reta; for (i = valid_queue_count, j = 0; i < dev_info.reta_size; i++, j++) { diff --git a/src/plugins/dpdk/device/init.c b/src/plugins/dpdk/device/init.c index 81803ab1cb4b..d07acd644a29 100644 --- a/src/plugins/dpdk/device/init.c +++ b/src/plugins/dpdk/device/init.c @@ -759,7 +759,7 @@ dpdk_lib_init (dpdk_main_t * dm) dpdk_device_setup (xd); /* rss queues should be configured after dpdk_device_setup() */ - if (devconf->rss_queues != NULL) + if ((hi != NULL) && (devconf->rss_queues != NULL)) { if (vnet_hw_interface_set_rss_queues (vnet_get_main (), hi, devconf->rss_queues)) diff --git a/src/plugins/ikev2/ikev2.api b/src/plugins/ikev2/ikev2.api index 1e1dbf9589df..9f44cba1661a 100644 --- a/src/plugins/ikev2/ikev2.api +++ b/src/plugins/ikev2/ikev2.api @@ -251,6 +251,20 @@ autoreply define ikev2_profile_set_id option status="in_progress"; }; +/** \brief IKEv2: Disable NAT traversal + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param name - IKEv2 profile name +*/ +autoreply define ikev2_profile_disable_natt +{ + u32 client_index; + u32 context; + + string name[64]; + option status="in_progress"; +}; + /** \brief IKEv2: Set IKEv2 profile traffic selector parameters @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -264,7 +278,7 @@ autoreply define ikev2_profile_set_ts string name[64]; vl_api_ikev2_ts_t ts; - option vat_help = "name protocol start_port end_port start_addr end_addr (local|remote)"; + option vat_help = "name protocol start_port end_port start_addr end_addr (local|remote)"; option status="in_progress"; }; diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index a9e78a3dfc3c..4e1984927756 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -62,7 +62,36 @@ format_ikev2_trace (u8 * s, va_list * args) return s; } -static vlib_node_registration_t ikev2_node; +#define IKEV2_GENERATE_SA_INIT_OK_str "" +#define IKEV2_GENERATE_SA_INIT_OK_ERR_NO_DH_STR \ + "no DH group configured for IKE proposals!" +#define IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR \ + "DH group not supported!" + +typedef enum +{ + IKEV2_GENERATE_SA_INIT_OK, + IKEV2_GENERATE_SA_INIT_ERR_NO_DH, + IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH, +} ikev2_generate_sa_error_t; + +static u8 * +format_ikev2_gen_sa_error (u8 * s, va_list * args) +{ + ikev2_generate_sa_error_t e = va_arg (*args, ikev2_generate_sa_error_t); + switch (e) + { + case IKEV2_GENERATE_SA_INIT_OK: + break; + case IKEV2_GENERATE_SA_INIT_ERR_NO_DH: + s = format (s, IKEV2_GENERATE_SA_INIT_OK_ERR_NO_DH_STR); + break; + case IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH: + s = format (s, IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR); + break; + } + return s; +} #define foreach_ikev2_error \ _(PROCESSED, "IKEv2 packets processed") \ @@ -71,7 +100,9 @@ _(IKE_SA_INIT_IGNORE, "IKE_SA_INIT ignore (IKE SA already auth)") \ _(IKE_REQ_RETRANSMIT, "IKE request retransmit") \ _(IKE_REQ_IGNORE, "IKE request ignore (old msgid)") \ _(NOT_IKEV2, "Non IKEv2 packets received") \ -_(BAD_LENGTH, "Bad packet length") +_(BAD_LENGTH, "Bad packet length") \ +_(MALFORMED_PACKET, "Malformed packet") \ +_(NO_BUFF_SPACE, "No buffer space") typedef enum { @@ -90,16 +121,23 @@ static char *ikev2_error_strings[] = { typedef enum { IKEV2_NEXT_IP4_LOOKUP, - IKEV2_NEXT_ERROR_DROP, - IKEV2_N_NEXT, -} ikev2_next_t; + IKEV2_NEXT_IP4_ERROR_DROP, + IKEV2_IP4_N_NEXT, +} ikev2_ip4_next_t; + +typedef enum +{ + IKEV2_NEXT_IP6_LOOKUP, + IKEV2_NEXT_IP6_ERROR_DROP, + IKEV2_IP6_N_NEXT, +} ikev2_ip6_next_t; typedef u32 ikev2_non_esp_marker; static_always_inline u16 ikev2_get_port (ikev2_sa_t * sa) { - return sa->natt ? IKEV2_PORT_NATT : IKEV2_PORT; + return ikev2_natt_active (sa) ? IKEV2_PORT_NATT : IKEV2_PORT; } static_always_inline int @@ -261,7 +299,7 @@ ikev2_sa_free_proposal_vector (ikev2_sa_proposal_t ** v) vec_free (p->transforms); } vec_free (*v); -}; +} static void ikev2_sa_free_child_sa (ikev2_child_sa_t * c) @@ -296,9 +334,12 @@ static void ikev2_sa_free_all_vec (ikev2_sa_t * sa) { vec_free (sa->i_nonce); - vec_free (sa->i_dh_data); + vec_free (sa->r_nonce); + vec_free (sa->dh_shared_key); vec_free (sa->dh_private_key); + vec_free (sa->i_dh_data); + vec_free (sa->r_dh_data); ikev2_sa_free_proposal_vector (&sa->r_proposals); ikev2_sa_free_proposal_vector (&sa->i_proposals); @@ -312,44 +353,50 @@ ikev2_sa_free_all_vec (ikev2_sa_t * sa) vec_free (sa->sk_pr); vec_free (sa->i_id.data); - vec_free (sa->i_auth.data); vec_free (sa->r_id.data); + + vec_free (sa->i_auth.data); + if (sa->i_auth.key) + EVP_PKEY_free (sa->i_auth.key); vec_free (sa->r_auth.data); if (sa->r_auth.key) EVP_PKEY_free (sa->r_auth.key); vec_free (sa->del); + vec_free (sa->rekey); + + vec_free (sa->last_sa_init_req_packet_data); + vec_free (sa->last_sa_init_res_packet_data); + + vec_free (sa->last_res_packet_data); + ikev2_sa_free_all_child_sa (&sa->childs); } static void -ikev2_delete_sa (ikev2_sa_t * sa) +ikev2_delete_sa (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa) { - ikev2_main_t *km = &ikev2_main; - u32 thread_index = vlib_get_thread_index (); uword *p; ikev2_sa_free_all_vec (sa); - p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, sa->rspi); + p = hash_get (ptd->sa_by_rspi, sa->rspi); if (p) { - hash_unset (km->per_thread_data[thread_index].sa_by_rspi, sa->rspi); - pool_put (km->per_thread_data[thread_index].sas, sa); + hash_unset (ptd->sa_by_rspi, sa->rspi); + pool_put (ptd->sas, sa); } } -static void +static ikev2_generate_sa_error_t ikev2_generate_sa_init_data (ikev2_sa_t * sa) { ikev2_sa_transform_t *t = 0, *t2; ikev2_main_t *km = &ikev2_main; if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE) - { - return; - } + return IKEV2_GENERATE_SA_INIT_ERR_NO_DH; /* check if received DH group is on our list of supported groups */ vec_foreach (t2, km->supported_transforms) @@ -364,7 +411,7 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa) if (!t) { sa->dh_group = IKEV2_TRANSFORM_DH_TYPE_NONE; - return; + return IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH; } if (sa->is_initiator) @@ -389,6 +436,7 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa) /* generate dh keys */ ikev2_generate_dh (sa, t); + return IKEV2_GENERATE_SA_INIT_OK; } static void @@ -402,14 +450,16 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) sa->i_nonce = _(sai->i_nonce); sa->i_dh_data = _(sai->i_dh_data); sa->dh_private_key = _(sai->dh_private_key); - sa->iaddr.as_u32 = sai->iaddr.as_u32; - sa->raddr.as_u32 = sai->raddr.as_u32; + ip_address_copy (&sa->iaddr, &sai->iaddr); + ip_address_copy (&sa->raddr, &sai->raddr); sa->is_initiator = sai->is_initiator; sa->i_id.type = sai->i_id.type; sa->r_id.type = sai->r_id.type; sa->profile_index = sai->profile_index; sa->tun_itf = sai->tun_itf; sa->is_tun_itf_set = sai->is_tun_itf_set; + if (sai->natt_state == IKEV2_NATT_DISABLED) + sa->natt_state = IKEV2_NATT_DISABLED; sa->i_id.data = _(sai->i_id.data); sa->r_id.data = _(sai->r_id.data); sa->i_auth.method = sai->i_auth.method; @@ -417,6 +467,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) sa->i_auth.data = _(sai->i_auth.data); sa->i_auth.key = _(sai->i_auth.key); sa->last_sa_init_req_packet_data = _(sai->last_sa_init_req_packet_data); + sa->last_init_msg_id = sai->last_init_msg_id; sa->childs = _(sai->childs); sa->udp_encap = sai->udp_encap; sa->ipsec_over_udp_port = sai->ipsec_over_udp_port; @@ -612,76 +663,122 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child) } static_always_inline u8 * -ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, u32 ip, u16 port) +ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t * ia, u16 port) { - /* ispi, rspi, ip, port */ - u8 buf[8 + 8 + 4 + 2]; + const u32 max_buf_size = + sizeof (ispi) + sizeof (rspi) + sizeof (ip6_address_t) + sizeof (u16); + u8 buf[max_buf_size]; u8 *res = vec_new (u8, 20); clib_memcpy_fast (&buf[0], &ispi, sizeof (ispi)); clib_memcpy_fast (&buf[8], &rspi, sizeof (rspi)); - clib_memcpy_fast (&buf[8 + 8], &ip, sizeof (ip)); - clib_memcpy_fast (&buf[8 + 8 + 4], &port, sizeof (port)); - SHA1 (buf, sizeof (buf), res); + clib_memcpy_fast (&buf[8 + 8], ip_addr_bytes (ia), ip_address_size (ia)); + clib_memcpy_fast (&buf[8 + 8 + ip_address_size (ia)], &port, sizeof (port)); + SHA1 (buf, 2 * sizeof (ispi) + sizeof (port) + ip_address_size (ia), res); return res; } -static void -ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, - ike_header_t * ike, udp_header_t * udp, u32 len) +static int +ikev2_parse_ke_payload (const void *p, u32 rlen, ikev2_sa_t * sa, + u8 ** ke_data) +{ + const ike_ke_payload_header_t *ke = p; + u16 plen = clib_net_to_host_u16 (ke->length); + ASSERT (plen >= sizeof (*ke) && plen <= rlen); + if (sizeof (*ke) > rlen) + return 0; + + sa->dh_group = clib_net_to_host_u16 (ke->dh_group); + vec_reset_length (ke_data[0]); + vec_add (ke_data[0], ke->payload, plen - sizeof (*ke)); + return 1; +} + +static int +ikev2_parse_nonce_payload (const void *p, u32 rlen, u8 * nonce) +{ + const ike_payload_header_t *ikep = p; + u16 plen = clib_net_to_host_u16 (ikep->length); + ASSERT (plen >= sizeof (*ikep) && plen <= rlen); + clib_memcpy_fast (nonce, ikep->payload, plen - sizeof (*ikep)); + return 1; +} + +static int +ikev2_check_payload_length (const ike_payload_header_t * ikep, int rlen, + u16 * plen) +{ + if (sizeof (*ikep) > rlen) + return 0; + *plen = clib_net_to_host_u16 (ikep->length); + if (*plen < sizeof (*ikep) || *plen > rlen) + return 0; + return 1; +} + +static int +ikev2_process_sa_init_req (vlib_main_t * vm, + ikev2_sa_t * sa, ike_header_t * ike, + udp_header_t * udp, u32 len) { + u8 nonce[IKEV2_NONCE_SIZE]; int p = 0; u8 payload = ike->nextpayload; + ike_payload_header_t *ikep; + u16 plen; ikev2_elog_exchange ("ispi %lx rspi %lx IKE_INIT request received " - "from %d.%d.%d.%d", - clib_net_to_host_u64 (ike->ispi), - clib_net_to_host_u64 (ike->rspi), sa->iaddr.as_u32); + "from ", clib_net_to_host_u64 (ike->ispi), + clib_net_to_host_u64 (ike->rspi), + ip_addr_v4 (&sa->iaddr).as_u32, + ip_addr_version (&sa->iaddr) == AF_IP4); sa->ispi = clib_net_to_host_u64 (ike->ispi); /* store whole IKE payload - needed for PSK auth */ - vec_free (sa->last_sa_init_req_packet_data); + vec_reset_length (sa->last_sa_init_req_packet_data); vec_add (sa->last_sa_init_req_packet_data, ike, len); + if (len < sizeof (*ike)) + return 0; + + len -= sizeof (*ike); while (p < len && payload != IKEV2_PAYLOAD_NONE) { - ike_payload_header_t *ikep = (ike_payload_header_t *) & ike->payload[p]; - u32 plen = clib_net_to_host_u16 (ikep->length); - - if (plen < sizeof (ike_payload_header_t)) - return; + ikep = (ike_payload_header_t *) & ike->payload[p]; + int current_length = len - p; + if (!ikev2_check_payload_length (ikep, current_length, &plen)) + return 0; if (payload == IKEV2_PAYLOAD_SA) { ikev2_sa_free_proposal_vector (&sa->i_proposals); - sa->i_proposals = ikev2_parse_sa_payload (ikep); + sa->i_proposals = ikev2_parse_sa_payload (ikep, current_length); } else if (payload == IKEV2_PAYLOAD_KE) { - ike_ke_payload_header_t *ke = (ike_ke_payload_header_t *) ikep; - sa->dh_group = clib_net_to_host_u16 (ke->dh_group); - vec_free (sa->i_dh_data); - vec_add (sa->i_dh_data, ke->payload, plen - sizeof (*ke)); + if (!ikev2_parse_ke_payload (ikep, current_length, sa, + &sa->i_dh_data)) + return 0; } else if (payload == IKEV2_PAYLOAD_NONCE) { - vec_free (sa->i_nonce); - vec_add (sa->i_nonce, ikep->payload, plen - sizeof (*ikep)); + vec_reset_length (sa->i_nonce); + if (ikev2_parse_nonce_payload (ikep, current_length, nonce)) + vec_add (sa->i_nonce, nonce, plen - sizeof (*ikep)); } else if (payload == IKEV2_PAYLOAD_NOTIFY) { - ikev2_notify_t *n = ikev2_parse_notify_payload (ikep); + ikev2_notify_t *n = + ikev2_parse_notify_payload (ikep, current_length); if (n->msg_type == IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP) { - u8 *src_sha = - ikev2_compute_nat_sha1 (clib_net_to_host_u64 (ike->ispi), 0, - clib_net_to_host_u32 (sa-> - iaddr.as_u32), - udp->src_port); + u8 *src_sha = ikev2_compute_nat_sha1 (ike->ispi, 0, &sa->iaddr, + udp->src_port); if (clib_memcmp (src_sha, n->data, vec_len (src_sha))) { - sa->natt = 1; + if (sa->natt_state == IKEV2_NATT_ENABLED) + sa->natt_state = IKEV2_NATT_ACTIVE; ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx initiator" " behind NAT", sa->ispi); } @@ -690,14 +787,12 @@ ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, else if (n->msg_type == IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP) { - u8 *dst_sha = - ikev2_compute_nat_sha1 (clib_net_to_host_u64 (ike->ispi), 0, - clib_net_to_host_u32 (sa-> - raddr.as_u32), - udp->dst_port); + u8 *dst_sha = ikev2_compute_nat_sha1 (ike->ispi, 0, &sa->raddr, + udp->dst_port); if (clib_memcmp (dst_sha, n->data, vec_len (dst_sha))) { - sa->natt = 1; + if (sa->natt_state == IKEV2_NATT_ENABLED) + sa->natt_state = IKEV2_NATT_ACTIVE; ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx responder" " (self) behind NAT", sa->ispi); } @@ -717,7 +812,7 @@ ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, { ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); sa->unsupported_cp = payload; - return; + return 0; } } @@ -726,38 +821,47 @@ ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, } ikev2_set_state (sa, IKEV2_STATE_SA_INIT); + return 1; } static void -ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, - ike_header_t * ike, udp_header_t * udp, u32 len) +ikev2_process_sa_init_resp (vlib_main_t * vm, + ikev2_sa_t * sa, ike_header_t * ike, + udp_header_t * udp, u32 len) { + u8 nonce[IKEV2_NONCE_SIZE]; int p = 0; u8 payload = ike->nextpayload; + ike_payload_header_t *ikep; + u16 plen; sa->ispi = clib_net_to_host_u64 (ike->ispi); sa->rspi = clib_net_to_host_u64 (ike->rspi); ikev2_elog_exchange ("ispi %lx rspi %lx IKE_INIT response received " - "from %d.%d.%d.%d", sa->ispi, sa->rspi, - sa->raddr.as_u32); + "from ", sa->ispi, sa->rspi, + ip_addr_v4 (&sa->raddr).as_u32, + ip_addr_version (&sa->raddr) == AF_IP4); /* store whole IKE payload - needed for PSK auth */ - vec_free (sa->last_sa_init_res_packet_data); + vec_reset_length (sa->last_sa_init_res_packet_data); vec_add (sa->last_sa_init_res_packet_data, ike, len); + if (sizeof (*ike) > len) + return; + + len -= sizeof (*ike); while (p < len && payload != IKEV2_PAYLOAD_NONE) { - ike_payload_header_t *ikep = (ike_payload_header_t *) & ike->payload[p]; - u32 plen = clib_net_to_host_u16 (ikep->length); - - if (plen < sizeof (ike_payload_header_t)) + int current_length = len - p; + ikep = (ike_payload_header_t *) & ike->payload[p]; + if (!ikev2_check_payload_length (ikep, current_length, &plen)) return; if (payload == IKEV2_PAYLOAD_SA) { ikev2_sa_free_proposal_vector (&sa->r_proposals); - sa->r_proposals = ikev2_parse_sa_payload (ikep); + sa->r_proposals = ikev2_parse_sa_payload (ikep, current_length); if (sa->r_proposals) { ikev2_set_state (sa, IKEV2_STATE_SA_INIT); @@ -767,25 +871,24 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, } else if (payload == IKEV2_PAYLOAD_KE) { - ike_ke_payload_header_t *ke = (ike_ke_payload_header_t *) ikep; - sa->dh_group = clib_net_to_host_u16 (ke->dh_group); - vec_free (sa->r_dh_data); - vec_add (sa->r_dh_data, ke->payload, plen - sizeof (*ke)); + if (!ikev2_parse_ke_payload (ikep, current_length, sa, + &sa->r_dh_data)) + return; } else if (payload == IKEV2_PAYLOAD_NONCE) { - vec_free (sa->r_nonce); - vec_add (sa->r_nonce, ikep->payload, plen - sizeof (*ikep)); + vec_reset_length (sa->r_nonce); + if (ikev2_parse_nonce_payload (ikep, current_length, nonce)) + vec_add (sa->r_nonce, nonce, plen - sizeof (*ikep)); } else if (payload == IKEV2_PAYLOAD_NOTIFY) { - ikev2_notify_t *n = ikev2_parse_notify_payload (ikep); + ikev2_notify_t *n = + ikev2_parse_notify_payload (ikep, current_length); if (n->msg_type == IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP) { - u8 *src_sha = ikev2_compute_nat_sha1 (ike->ispi, - ike->rspi, - clib_net_to_host_u32 - (sa->raddr.as_u32), + u8 *src_sha = ikev2_compute_nat_sha1 (ike->ispi, ike->rspi, + &sa->raddr, udp->src_port); if (clib_memcmp (src_sha, n->data, vec_len (src_sha))) { @@ -797,13 +900,13 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, else if (n->msg_type == IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP) { - u8 *dst_sha = ikev2_compute_nat_sha1 (ike->ispi, - ike->rspi, - sa->iaddr.as_u32, + u8 *dst_sha = ikev2_compute_nat_sha1 (ike->ispi, ike->rspi, + &sa->iaddr, udp->dst_port); if (clib_memcmp (dst_sha, n->data, vec_len (dst_sha))) { - sa->natt = 1; + if (sa->natt_state == IKEV2_NATT_ENABLED) + sa->natt_state = IKEV2_NATT_ACTIVE; ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx initiator" " (self) behind NAT", sa->ispi); } @@ -833,15 +936,15 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, } static u8 * -ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload, - u32 len) +ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, + u8 * payload, u32 rlen, u32 * out_len) { ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data (); int p = 0; - u8 last_payload = 0, *plaintext = 0; - u8 *hmac = 0; + u8 last_payload = 0, *hmac = 0, *plaintext = 0; ike_payload_header_t *ikep = 0; - u32 plen = 0; + u16 plen = 0; + u32 dlen = 0; ikev2_sa_transform_t *tr_integ; ikev2_sa_transform_t *tr_encr; tr_integ = @@ -850,16 +953,19 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload, ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); int is_aead = tr_encr->encr_type == IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16; - if ((!sa->sk_ar || !sa->sk_ai) && !is_aead) + if (((!sa->sk_ar || !sa->sk_ai) && !is_aead) || (!sa->sk_ei || !sa->sk_er)) + return 0; + + if (rlen <= sizeof (*ike)) return 0; + int len = rlen - sizeof (*ike); while (p < len && *payload != IKEV2_PAYLOAD_NONE && last_payload != IKEV2_PAYLOAD_SK) { ikep = (ike_payload_header_t *) & ike->payload[p]; - plen = clib_net_to_host_u16 (ikep->length); - - if (plen < sizeof (*ikep)) + int current_length = len - p; + if (!ikev2_check_payload_length (ikep, current_length, &plen)) return 0; if (*payload == IKEV2_PAYLOAD_SK) @@ -897,24 +1003,29 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload, u32 aad_len = ikep->payload - aad; u8 *tag = ikep->payload + plen; - plaintext = ikev2_decrypt_aead_data (ptd, sa, tr_encr, ikep->payload, - plen, aad, aad_len, tag); + int rc = ikev2_decrypt_aead_data (ptd, sa, tr_encr, ikep->payload, + plen, aad, aad_len, tag, &dlen); + if (rc) + { + *out_len = dlen; + plaintext = ikep->payload + IKEV2_GCM_IV_SIZE; + } } else { - if (len < tr_integ->key_trunc) + if (rlen < tr_integ->key_trunc) return 0; hmac = ikev2_calc_integr (tr_integ, sa->is_initiator ? sa->sk_ar : sa->sk_ai, - (u8 *) ike, len - tr_integ->key_trunc); + (u8 *) ike, rlen - tr_integ->key_trunc); if (plen < sizeof (*ikep) + tr_integ->key_trunc) return 0; plen = plen - sizeof (*ikep) - tr_integ->key_trunc; - if (memcmp (hmac, &ikep->payload[plen], tr_integ->key_trunc)) + if (clib_memcmp (hmac, &ikep->payload[plen], tr_integ->key_trunc)) { ikev2_elog_error ("message integrity check failed"); vec_free (hmac); @@ -922,7 +1033,13 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload, } vec_free (hmac); - plaintext = ikev2_decrypt_data (ptd, sa, tr_encr, ikep->payload, plen); + int rc = ikev2_decrypt_data (ptd, sa, tr_encr, ikep->payload, plen, + &dlen); + if (rc) + { + *out_len = dlen; + plaintext = ikep->payload + tr_encr->block_size; + } } return plaintext; @@ -937,44 +1054,41 @@ ikev2_is_id_equal (ikev2_id_t * i1, ikev2_id_t * i2) if (vec_len (i1->data) != vec_len (i2->data)) return 0; - if (memcmp (i1->data, i2->data, vec_len (i1->data))) + if (clib_memcmp (i1->data, i2->data, vec_len (i1->data))) return 0; return 1; } static void -ikev2_initial_contact_cleanup (ikev2_sa_t * sa) +ikev2_initial_contact_cleanup_internal (ikev2_main_per_thread_data_t * ptd, + ikev2_sa_t * sa) { ikev2_main_t *km = &ikev2_main; ikev2_sa_t *tmp; u32 i, *delete = 0; ikev2_child_sa_t *c; - u32 thread_index = vlib_get_thread_index (); - - if (!sa->initial_contact) - return; /* find old IKE SAs with the same authenticated identity */ /* *INDENT-OFF* */ - pool_foreach (tmp, km->per_thread_data[thread_index].sas, ({ - if (!ikev2_is_id_equal (&tmp->i_id, &sa->i_id) - || !ikev2_is_id_equal(&tmp->r_id, &sa->r_id)) - continue; + pool_foreach (tmp, ptd->sas, ({ + if (!ikev2_is_id_equal (&tmp->i_id, &sa->i_id) + || !ikev2_is_id_equal(&tmp->r_id, &sa->r_id)) + continue; - if (sa->rspi != tmp->rspi) - vec_add1(delete, tmp - km->per_thread_data[thread_index].sas); + if (sa->rspi != tmp->rspi) + vec_add1(delete, tmp - ptd->sas); })); /* *INDENT-ON* */ for (i = 0; i < vec_len (delete); i++) { - tmp = - pool_elt_at_index (km->per_thread_data[thread_index].sas, delete[i]); - vec_foreach (c, - tmp->childs) ikev2_delete_tunnel_interface (km->vnet_main, - tmp, c); - ikev2_delete_sa (tmp); + tmp = pool_elt_at_index (ptd->sas, delete[i]); + vec_foreach (c, tmp->childs) + { + ikev2_delete_tunnel_interface (km->vnet_main, tmp, c); + } + ikev2_delete_sa (ptd, tmp); } vec_free (delete); @@ -982,31 +1096,85 @@ ikev2_initial_contact_cleanup (ikev2_sa_t * sa) } static void -ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, - u32 len) +ikev2_initial_contact_cleanup (ikev2_main_per_thread_data_t * ptd, + ikev2_sa_t * sa) +{ + ikev2_main_t *km = &ikev2_main; + + if (!sa->initial_contact) + return; + + if (ptd) + { + ikev2_initial_contact_cleanup_internal (ptd, sa); + } + else + { + vec_foreach (ptd, km->per_thread_data) + ikev2_initial_contact_cleanup_internal (ptd, sa); + } + sa->initial_contact = 0; +} + +static int +ikev2_parse_id_payload (const void *p, u16 rlen, ikev2_id_t * sa_id) +{ + const ike_id_payload_header_t *id = p; + u16 plen = clib_net_to_host_u16 (id->length); + if (plen < sizeof (*id) || plen > rlen) + return 0; + + sa_id->type = id->id_type; + vec_reset_length (sa_id->data); + vec_add (sa_id->data, id->payload, plen - sizeof (*id)); + + return 1; +} + +static int +ikev2_parse_auth_payload (const void *p, u32 rlen, ikev2_auth_t * a) +{ + const ike_auth_payload_header_t *ah = p; + u16 plen = clib_net_to_host_u16 (ah->length); + + a->method = ah->auth_method; + vec_reset_length (a->data); + vec_add (a->data, ah->payload, plen - sizeof (*ah)); + return 1; +} + +static int +ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, + ike_header_t * ike, u32 len) { - ikev2_child_sa_t *first_child_sa; int p = 0; + ikev2_child_sa_t *first_child_sa; u8 payload = ike->nextpayload; u8 *plaintext = 0; ike_payload_header_t *ikep; - u32 plen; + u16 plen; + u32 dlen = 0; ikev2_elog_exchange ("ispi %lx rspi %lx EXCHANGE_IKE_AUTH received " - "from %d.%d.%d.%d", clib_host_to_net_u64 (ike->ispi), + "from ", clib_host_to_net_u64 (ike->ispi), clib_host_to_net_u64 (ike->rspi), - sa->is_initiator ? sa->raddr.as_u32 : sa-> - iaddr.as_u32); + sa->is_initiator ? + ip_addr_v4 (&sa->raddr).as_u32 : + ip_addr_v4 (&sa->iaddr).as_u32, + ip_addr_version (&sa->raddr) == AF_IP4); ikev2_calc_keys (sa); - plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len); + plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len, &dlen); if (!plaintext) { if (sa->unsupported_cp) - ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); - goto cleanup_and_exit; + { + ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); + return 0; + } + goto malformed; } /* select or create 1st child SA */ @@ -1022,64 +1190,57 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, /* process encrypted payload */ - p = 0; - while (p < vec_len (plaintext) && payload != IKEV2_PAYLOAD_NONE) + while (p < dlen && payload != IKEV2_PAYLOAD_NONE) { ikep = (ike_payload_header_t *) & plaintext[p]; - plen = clib_net_to_host_u16 (ikep->length); - - if (plen < sizeof (ike_payload_header_t)) - goto cleanup_and_exit; + int current_length = dlen - p; + if (!ikev2_check_payload_length (ikep, current_length, &plen)) + goto malformed; if (payload == IKEV2_PAYLOAD_SA) /* 33 */ { if (sa->is_initiator) { ikev2_sa_free_proposal_vector (&first_child_sa->r_proposals); - first_child_sa->r_proposals = ikev2_parse_sa_payload (ikep); + first_child_sa->r_proposals = ikev2_parse_sa_payload (ikep, + current_length); } else { ikev2_sa_free_proposal_vector (&first_child_sa->i_proposals); - first_child_sa->i_proposals = ikev2_parse_sa_payload (ikep); + first_child_sa->i_proposals = ikev2_parse_sa_payload (ikep, + current_length); } } else if (payload == IKEV2_PAYLOAD_IDI) /* 35 */ { - ike_id_payload_header_t *id = (ike_id_payload_header_t *) ikep; - - sa->i_id.type = id->id_type; - vec_free (sa->i_id.data); - vec_add (sa->i_id.data, id->payload, plen - sizeof (*id)); + if (!ikev2_parse_id_payload (ikep, current_length, &sa->i_id)) + goto malformed; } else if (payload == IKEV2_PAYLOAD_IDR) /* 36 */ { - ike_id_payload_header_t *id = (ike_id_payload_header_t *) ikep; - - sa->r_id.type = id->id_type; - vec_free (sa->r_id.data); - vec_add (sa->r_id.data, id->payload, plen - sizeof (*id)); + if (!ikev2_parse_id_payload (ikep, current_length, &sa->r_id)) + goto malformed; } else if (payload == IKEV2_PAYLOAD_AUTH) /* 39 */ { - ike_auth_payload_header_t *a = (ike_auth_payload_header_t *) ikep; - if (sa->is_initiator) { - sa->r_auth.method = a->auth_method; - vec_free (sa->r_auth.data); - vec_add (sa->r_auth.data, a->payload, plen - sizeof (*a)); + if (!ikev2_parse_auth_payload (ikep, current_length, + &sa->r_auth)) + goto malformed; } else { - sa->i_auth.method = a->auth_method; - vec_free (sa->i_auth.data); - vec_add (sa->i_auth.data, a->payload, plen - sizeof (*a)); + if (!ikev2_parse_auth_payload (ikep, current_length, + &sa->i_auth)) + goto malformed; } } else if (payload == IKEV2_PAYLOAD_NOTIFY) /* 41 */ { - ikev2_notify_t *n = ikev2_parse_notify_payload (ikep); + ikev2_notify_t *n = + ikev2_parse_notify_payload (ikep, current_length); if (n->msg_type == IKEV2_NOTIFY_MSG_INITIAL_CONTACT) { sa->initial_contact = 1; @@ -1093,12 +1254,12 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, else if (payload == IKEV2_PAYLOAD_TSI) /* 44 */ { vec_free (first_child_sa->tsi); - first_child_sa->tsi = ikev2_parse_ts_payload (ikep); + first_child_sa->tsi = ikev2_parse_ts_payload (ikep, current_length); } else if (payload == IKEV2_PAYLOAD_TSR) /* 45 */ { vec_free (first_child_sa->tsr); - first_child_sa->tsr = ikev2_parse_ts_payload (ikep); + first_child_sa->tsr = ikev2_parse_ts_payload (ikep, current_length); } else { @@ -1109,7 +1270,7 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, { ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); sa->unsupported_cp = payload; - return; + return 0; } } @@ -1117,50 +1278,62 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, p += plen; } -cleanup_and_exit: - vec_free (plaintext); + return 1; + +malformed: + ikev2_set_state (sa, IKEV2_STATE_DELETED); + return 0; } -static void -ikev2_process_informational_req (vlib_main_t * vm, ikev2_sa_t * sa, - ike_header_t * ike, u32 len) +static int +ikev2_process_informational_req (vlib_main_t * vm, + ikev2_sa_t * sa, ike_header_t * ike, u32 len) { int p = 0; u8 payload = ike->nextpayload; u8 *plaintext = 0; ike_payload_header_t *ikep; - u32 plen; + u32 dlen = 0; + ikev2_notify_t *n = 0; sa->liveness_retries = 0; ikev2_elog_exchange ("ispi %lx rspi %lx INFORMATIONAL received " - "from %d.%d.%d.%d", clib_host_to_net_u64 (ike->ispi), - clib_host_to_net_u64 (ike->rspi), sa->iaddr.as_u32); + "from ", clib_host_to_net_u64 (ike->ispi), + clib_host_to_net_u64 (ike->rspi), + ip_addr_v4 (&sa->iaddr).as_u32, + ip_addr_version (&sa->iaddr) == AF_IP4); - plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len); + plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len, &dlen); if (!plaintext) - goto cleanup_and_exit; + return 0; /* process encrypted payload */ p = 0; - while (p < vec_len (plaintext) && payload != IKEV2_PAYLOAD_NONE) + while (p < dlen && payload != IKEV2_PAYLOAD_NONE) { + u32 current_length = dlen - p; + if (p + sizeof (*ikep) > dlen) + return 0; + ikep = (ike_payload_header_t *) & plaintext[p]; - plen = clib_net_to_host_u16 (ikep->length); + u16 plen = clib_net_to_host_u16 (ikep->length); - if (plen < sizeof (ike_payload_header_t)) - goto cleanup_and_exit; + if (plen < sizeof (*ikep) || plen > current_length) + return 0; if (payload == IKEV2_PAYLOAD_NOTIFY) /* 41 */ { - ikev2_notify_t *n = ikev2_parse_notify_payload (ikep); + n = ikev2_parse_notify_payload (ikep, current_length); + if (!n) + return 0; if (n->msg_type == IKEV2_NOTIFY_MSG_AUTHENTICATION_FAILED) ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); vec_free (n); } else if (payload == IKEV2_PAYLOAD_DELETE) /* 42 */ { - sa->del = ikev2_parse_delete_payload (ikep); + sa->del = ikev2_parse_delete_payload (ikep, current_length); } else if (payload == IKEV2_PAYLOAD_VENDOR) /* 43 */ { @@ -1173,21 +1346,19 @@ ikev2_process_informational_req (vlib_main_t * vm, ikev2_sa_t * sa, if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { sa->unsupported_cp = payload; - return; + return 0; } } - payload = ikep->nextpayload; p += plen; } - -cleanup_and_exit: - vec_free (plaintext); + return 1; } -static void -ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, - ike_header_t * ike, u32 len) +static int +ikev2_process_create_child_sa_req (vlib_main_t * vm, + ikev2_sa_t * sa, ike_header_t * ike, + u32 len) { int p = 0; u8 payload = ike->nextpayload; @@ -1196,39 +1367,41 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, u8 nonce[IKEV2_NONCE_SIZE]; ike_payload_header_t *ikep; - u32 plen; ikev2_notify_t *n = 0; ikev2_ts_t *tsi = 0; ikev2_ts_t *tsr = 0; ikev2_sa_proposal_t *proposal = 0; ikev2_child_sa_t *child_sa; + u32 dlen = 0; + u16 plen; ikev2_elog_exchange ("ispi %lx rspi %lx CREATE_CHILD_SA received " - "from %d.%d.%d.%d", clib_host_to_net_u64 (ike->ispi), - clib_host_to_net_u64 (ike->rspi), sa->raddr.as_u32); + "from ", clib_host_to_net_u64 (ike->ispi), + clib_host_to_net_u64 (ike->rspi), + ip_addr_v4 (&sa->raddr).as_u32, + ip_addr_version (&sa->raddr) == AF_IP4); - plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len); + plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len, &dlen); if (!plaintext) goto cleanup_and_exit; /* process encrypted payload */ p = 0; - while (p < vec_len (plaintext) && payload != IKEV2_PAYLOAD_NONE) + while (payload != IKEV2_PAYLOAD_NONE) { ikep = (ike_payload_header_t *) & plaintext[p]; - plen = clib_net_to_host_u16 (ikep->length); - - if (plen < sizeof (ike_payload_header_t)) + int current_length = dlen - p; + if (!ikev2_check_payload_length (ikep, current_length, &plen)) goto cleanup_and_exit; - else if (payload == IKEV2_PAYLOAD_SA) + if (payload == IKEV2_PAYLOAD_SA) { - proposal = ikev2_parse_sa_payload (ikep); + proposal = ikev2_parse_sa_payload (ikep, current_length); } else if (payload == IKEV2_PAYLOAD_NOTIFY) { - n = ikev2_parse_notify_payload (ikep); + n = ikev2_parse_notify_payload (ikep, current_length); if (n->msg_type == IKEV2_NOTIFY_MSG_REKEY_SA) { rekeying = 1; @@ -1236,7 +1409,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, } else if (payload == IKEV2_PAYLOAD_DELETE) { - sa->del = ikev2_parse_delete_payload (ikep); + sa->del = ikev2_parse_delete_payload (ikep, current_length); } else if (payload == IKEV2_PAYLOAD_VENDOR) { @@ -1244,15 +1417,15 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, } else if (payload == IKEV2_PAYLOAD_NONCE) { - clib_memcpy_fast (nonce, ikep->payload, plen - sizeof (*ikep)); + ikev2_parse_nonce_payload (ikep, current_length, nonce); } else if (payload == IKEV2_PAYLOAD_TSI) { - tsi = ikev2_parse_ts_payload (ikep); + tsi = ikev2_parse_ts_payload (ikep, current_length); } else if (payload == IKEV2_PAYLOAD_TSR) { - tsr = ikev2_parse_ts_payload (ikep); + tsr = ikev2_parse_ts_payload (ikep, current_length); } else { @@ -1264,14 +1437,16 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, goto cleanup_and_exit; } } - payload = ikep->nextpayload; p += plen; } - if (sa->is_initiator && proposal->protocol_id == IKEV2_PROTOCOL_ESP) + if (sa->is_initiator && proposal + && proposal->protocol_id == IKEV2_PROTOCOL_ESP) { - ikev2_rekey_t *rekey = &sa->rekey[0]; + ikev2_rekey_t *rekey = sa->rekey; + if (vec_len (rekey) == 0) + goto cleanup_and_exit; rekey->protocol_id = proposal->protocol_id; rekey->i_proposal = ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP); @@ -1280,7 +1455,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, rekey->tsi = tsi; rekey->tsr = tsr; /* update Nr */ - vec_free (sa->r_nonce); + vec_reset_length (sa->r_nonce); vec_add (sa->r_nonce, nonce, IKEV2_NONCE_SIZE); child_sa = ikev2_sa_get_child (sa, rekey->ispi, IKEV2_PROTOCOL_ESP, 1); if (child_sa) @@ -1307,17 +1482,23 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, rekey->tsi = tsi; rekey->tsr = tsr; /* update Ni */ - vec_free (sa->i_nonce); + vec_reset_length (sa->i_nonce); vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE); /* generate new Nr */ - vec_free (sa->r_nonce); - sa->r_nonce = vec_new (u8, IKEV2_NONCE_SIZE); + vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1); RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE); } + else + goto cleanup_and_exit; + vec_free (n); + return 1; cleanup_and_exit: - vec_free (plaintext); vec_free (n); + vec_free (proposal); + vec_free (tsr); + vec_free (tsi); + return 0; } static u8 * @@ -1368,8 +1549,8 @@ ikev2_ts_cmp (ikev2_ts_t * ts1, ikev2_ts_t * ts2) { if (ts1->ts_type == ts2->ts_type && ts1->protocol_id == ts2->protocol_id && ts1->start_port == ts2->start_port && ts1->end_port == ts2->end_port && - ts1->start_addr.as_u32 == ts2->start_addr.as_u32 && - ts1->end_addr.as_u32 == ts2->end_addr.as_u32) + !ip_address_cmp (&ts1->start_addr, &ts2->start_addr) && + !ip_address_cmp (&ts1->end_addr, &ts2->end_addr)) return 1; return 0; @@ -1503,7 +1684,7 @@ ikev2_sa_auth (ikev2_sa_t * sa) psk = ikev2_calc_prf(tr_prf, p->auth.data, key_pad); auth = ikev2_calc_prf(tr_prf, psk, authmsg); - if (!memcmp(auth, sa_auth->data, vec_len(sa_auth->data))) + if (!clib_memcmp(auth, sa_auth->data, vec_len(sa_auth->data))) { ikev2_set_state(sa, IKEV2_STATE_AUTHENTICATED); vec_free(auth); @@ -1551,11 +1732,13 @@ ikev2_sa_auth (ikev2_sa_t * sa) authmsg = ikev2_sa_generate_authmsg (sa, 1); if (sel_p->auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) { + vec_free (sa->r_auth.data); sa->r_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg); sa->r_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC; } else if (sel_p->auth.method == IKEV2_AUTH_METHOD_RSA_SIG) { + vec_free (sa->r_auth.data); sa->r_auth.data = ikev2_calc_sign (km->pkey, authmsg); sa->r_auth.method = IKEV2_AUTH_METHOD_RSA_SIG; } @@ -1612,11 +1795,13 @@ ikev2_sa_auth_init (ikev2_sa_t * sa) if (sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) { + vec_free (sa->i_auth.data); sa->i_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg); sa->i_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC; } else if (sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG) { + vec_free (sa->i_auth.data); sa->i_auth.data = ikev2_calc_sign (km->pkey, authmsg); sa->i_auth.method = IKEV2_AUTH_METHOD_RSA_SIG; } @@ -1691,9 +1876,9 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) if (rv) { - ikev2_elog_peers (IKEV2_LOG_ERROR, "installing ipip tunnel failed! " - "loc:%d.%d.%d.%d rem:%d.%d.%d.%d", - a->local_ip.ip4.as_u32, a->remote_ip.ip4.as_u32); + ikev2_elog_uint (IKEV2_LOG_ERROR, + "installing ipip tunnel failed! local spi: %x", + a->local_spi); return; } @@ -1710,30 +1895,46 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) vec_add1 (sas_in, a->old_remote_sa_id); } - rv |= ipsec_sa_add_and_lock (a->local_sa_id, - a->local_spi, - IPSEC_PROTOCOL_ESP, a->encr_type, - &a->loc_ckey, a->integ_type, &a->loc_ikey, - a->flags, 0, a->salt_local, &a->local_ip, - &a->remote_ip, NULL, a->src_port, a->dst_port); - rv |= ipsec_sa_add_and_lock (a->remote_sa_id, a->remote_spi, - IPSEC_PROTOCOL_ESP, a->encr_type, &a->rem_ckey, - a->integ_type, &a->rem_ikey, - (a->flags | IPSEC_SA_FLAG_IS_INBOUND), 0, - a->salt_remote, &a->remote_ip, - &a->local_ip, NULL, a->ipsec_over_udp_port, - a->ipsec_over_udp_port); - - rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in); + rv = ipsec_sa_add_and_lock (a->local_sa_id, + a->local_spi, + IPSEC_PROTOCOL_ESP, a->encr_type, + &a->loc_ckey, a->integ_type, &a->loc_ikey, + a->flags, 0, a->salt_local, &a->local_ip, + &a->remote_ip, NULL, a->src_port, a->dst_port); + if (rv) + goto err0; + + rv = ipsec_sa_add_and_lock (a->remote_sa_id, a->remote_spi, + IPSEC_PROTOCOL_ESP, a->encr_type, &a->rem_ckey, + a->integ_type, &a->rem_ikey, + (a->flags | IPSEC_SA_FLAG_IS_INBOUND), 0, + a->salt_remote, &a->remote_ip, + &a->local_ip, NULL, a->ipsec_over_udp_port, + a->ipsec_over_udp_port); + if (rv) + goto err1; + + rv = ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in); + if (rv) + goto err2; + + return; + +err2: + ipsec_sa_unlock_id (a->remote_sa_id); +err1: + ipsec_sa_unlock_id (a->local_sa_id); +err0: + vec_free (sas_in); } static int ikev2_create_tunnel_interface (vlib_main_t * vm, - u32 thread_index, ikev2_sa_t * sa, ikev2_child_sa_t * child, u32 sa_index, u32 child_index, u8 is_rekey) { + u32 thread_index = vlib_get_thread_index (); ikev2_main_t *km = &ikev2_main; ipsec_crypto_alg_t encr_type; ipsec_integ_alg_t integ_type; @@ -1753,16 +1954,16 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, if (sa->is_initiator) { - ip46_address_set_ip4 (&a.local_ip, &sa->iaddr); - ip46_address_set_ip4 (&a.remote_ip, &sa->raddr); + ip_address_to_46 (&sa->iaddr, &a.local_ip); + ip_address_to_46 (&sa->raddr, &a.remote_ip); proposals = child->r_proposals; a.local_spi = child->r_proposals[0].spi; a.remote_spi = child->i_proposals[0].spi; } else { - ip46_address_set_ip4 (&a.local_ip, &sa->raddr); - ip46_address_set_ip4 (&a.remote_ip, &sa->iaddr); + ip_address_to_46 (&sa->raddr, &a.local_ip); + ip_address_to_46 (&sa->iaddr, &a.remote_ip); proposals = child->i_proposals; a.local_spi = child->i_proposals[0].spi; a.remote_spi = child->r_proposals[0].spi; @@ -1774,7 +1975,7 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, a.flags |= IPSEC_SA_FLAG_IS_TUNNEL; a.flags |= IPSEC_SA_FLAG_UDP_ENCAP; } - if (sa->natt) + if (ikev2_natt_active (sa)) a.flags |= IPSEC_SA_FLAG_UDP_ENCAP; a.is_rekey = is_rekey; @@ -1900,7 +2101,8 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, a.salt_remote = child->salt_ei; a.salt_local = child->salt_er; } - a.dst_port = sa->natt ? sa->dst_port : sa->ipsec_over_udp_port; + a.dst_port = + ikev2_natt_active (sa) ? sa->dst_port : sa->ipsec_over_udp_port; a.src_port = sa->ipsec_over_udp_port; } @@ -2042,13 +2244,13 @@ ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, if (sa->is_initiator) { - ip46_address_set_ip4 (&a.local_ip, &sa->iaddr); - ip46_address_set_ip4 (&a.remote_ip, &sa->raddr); + ip_address_to_46 (&sa->iaddr, &a.local_ip); + ip_address_to_46 (&sa->raddr, &a.remote_ip); } else { - ip46_address_set_ip4 (&a.local_ip, &sa->raddr); - ip46_address_set_ip4 (&a.remote_ip, &sa->iaddr); + ip_address_to_46 (&sa->raddr, &a.local_ip); + ip_address_to_46 (&sa->iaddr, &a.remote_ip); } a.remote_sa_id = child->remote_sa_id; @@ -2061,9 +2263,11 @@ ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, } static u32 -ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, - udp_header_t * udp) +ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, + ike_header_t * ike, void *user, udp_header_t * udp) { + ikev2_main_t *km = &ikev2_main; + u16 buffer_data_size = vlib_buffer_get_default_data_size (km->vlib_main); v8 *integ = 0; ike_payload_header_t *ph; u16 plen; @@ -2126,8 +2330,7 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, u8 *nat_detection_sha1 = ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa->ispi), clib_host_to_net_u64 (sa->rspi), - clib_host_to_net_u32 (sa->raddr.as_u32), - udp->dst_port); + &sa->raddr, udp->dst_port); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP, nat_detection_sha1); @@ -2135,7 +2338,7 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, nat_detection_sha1 = ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa->ispi), clib_host_to_net_u64 (sa->rspi), - sa->iaddr.as_u32, udp->src_port); + &sa->iaddr, udp->src_port); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP, nat_detection_sha1); @@ -2210,7 +2413,7 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, { if (sa->del[0].protocol_id == IKEV2_PROTOCOL_IKE) { - if (sa->is_initiator) + if (ike_hdr_is_request (ike)) ikev2_payload_add_delete (chain, sa->del); /* The response to a request that deletes the IKE SA is an empty @@ -2304,26 +2507,26 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, ike->version = IKE_VERSION_2; ike->nextpayload = IKEV2_PAYLOAD_SK; tlen = sizeof (*ike); - if (sa->is_initiator) - { - ike->flags = IKEV2_HDR_FLAG_INITIATOR; - sa->last_init_msg_id = clib_net_to_host_u32 (ike->msgid); - } - else - { - ike->flags = IKEV2_HDR_FLAG_RESPONSE; - } + if (sa->is_initiator) + ike->flags |= IKEV2_HDR_FLAG_INITIATOR; if (ike->exchange == IKEV2_EXCHANGE_SA_INIT) { tlen += vec_len (chain->data); ike->nextpayload = chain->first_payload_type; ike->length = clib_host_to_net_u32 (tlen); + + if (tlen + b->current_length + b->current_data > buffer_data_size) + { + tlen = ~0; + goto done; + } + clib_memcpy_fast (ike->payload, chain->data, vec_len (chain->data)); /* store whole IKE payload - needed for PSK auth */ - vec_free (sa->last_sa_init_res_packet_data); + vec_reset_length (sa->last_sa_init_res_packet_data); vec_add (sa->last_sa_init_res_packet_data, ike, tlen); } else @@ -2348,21 +2551,36 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, plen += IKEV2_GCM_ICV_SIZE; tlen += plen; + if (tlen + b->current_length + b->current_data > buffer_data_size) + { + tlen = ~0; + goto done; + } + /* payload and total length */ ph->length = clib_host_to_net_u16 (plen); ike->length = clib_host_to_net_u32 (tlen); if (is_aead) { - ikev2_encrypt_aead_data (ptd, sa, tr_encr, chain->data, - ph->payload, (u8 *) ike, - sizeof (*ike) + sizeof (*ph), - ph->payload + plen - sizeof (*ph) - - IKEV2_GCM_ICV_SIZE); + if (!ikev2_encrypt_aead_data (ptd, sa, tr_encr, chain->data, + ph->payload, (u8 *) ike, + sizeof (*ike) + sizeof (*ph), + ph->payload + plen - sizeof (*ph) - + IKEV2_GCM_ICV_SIZE)) + { + tlen = ~0; + goto done; + } } else { - ikev2_encrypt_data (ptd, sa, tr_encr, chain->data, ph->payload); + if (!ikev2_encrypt_data + (ptd, sa, tr_encr, chain->data, ph->payload)) + { + tlen = ~0; + goto done; + } integ = ikev2_calc_integr (tr_integ, sa->is_initiator ? sa->sk_ai : sa->sk_ar, @@ -2372,7 +2590,7 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, } /* store whole IKE payload - needed for retransmit */ - vec_free (sa->last_res_packet_data); + vec_reset_length (sa->last_res_packet_data); vec_add (sa->last_res_packet_data, ike, tlen); } @@ -2383,70 +2601,87 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user, } static u32 -ikev2_retransmit_sa_init (ike_header_t * ike, - ip4_address_t iaddr, ip4_address_t raddr, u32 rlen) +ikev2_retransmit_sa_init_one (ikev2_sa_t * sa, ike_header_t * ike, + ip_address_t iaddr, ip_address_t raddr, + u32 rlen) { - ikev2_main_t *km = &ikev2_main; - ikev2_sa_t *sa; - u32 thread_index = vlib_get_thread_index (); + int p = 0; + ike_header_t *tmp; + u8 payload = ike->nextpayload; - /* *INDENT-OFF* */ - pool_foreach (sa, km->per_thread_data[thread_index].sas, ({ - if (sa->ispi == clib_net_to_host_u64(ike->ispi) && - sa->iaddr.as_u32 == iaddr.as_u32 && - sa->raddr.as_u32 == raddr.as_u32) - { - int p = 0; - u8 payload = ike->nextpayload; + if (sa->ispi != clib_net_to_host_u64 (ike->ispi) || + ip_address_cmp (&sa->iaddr, &iaddr) || + ip_address_cmp (&sa->raddr, &raddr)) + { + return 0; + } - while (p < rlen && payload!= IKEV2_PAYLOAD_NONE) { - ike_payload_header_t * ikep = (ike_payload_header_t *) &ike->payload[p]; - u32 plen = clib_net_to_host_u16(ikep->length); + while (p < rlen && payload != IKEV2_PAYLOAD_NONE) + { + ike_payload_header_t *ikep = (ike_payload_header_t *) & ike->payload[p]; + u32 plen = clib_net_to_host_u16 (ikep->length); - if (plen < sizeof(ike_payload_header_t)) - return -1; + if (plen < sizeof (ike_payload_header_t)) + return ~0; - if (payload == IKEV2_PAYLOAD_NONCE) - { - if (!memcmp(sa->i_nonce, ikep->payload, plen - sizeof(*ikep))) - { - /* req is retransmit */ - if (sa->state == IKEV2_STATE_SA_INIT) - { - ike_header_t * tmp = (ike_header_t*)sa->last_sa_init_res_packet_data; - u32 slen = clib_net_to_host_u32(tmp->length); - ike->ispi = tmp->ispi; - ike->rspi = tmp->rspi; - ike->nextpayload = tmp->nextpayload; - ike->version = tmp->version; - ike->exchange = tmp->exchange; - ike->flags = tmp->flags; - ike->msgid = tmp->msgid; - ike->length = tmp->length; - clib_memcpy_fast(ike->payload, tmp->payload, slen - sizeof(*ike)); - ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, - "ispi %lx IKE_SA_INIT retransmit " - "from %d.%d.%d.%d to %d.%d.%d.%d", - ike->ispi, - raddr.as_u32, iaddr.as_u32); - return slen; - } - /* else ignore req */ - else - { - ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, - "ispi %lx IKE_SA_INIT ignore " - "from %d.%d.%d.%d to %d.%d.%d.%d", - ike->ispi, - raddr.as_u32, iaddr.as_u32); - return ~0; - } - } - } - payload = ikep->nextpayload; - p+=plen; - } - } + if (payload == IKEV2_PAYLOAD_NONCE && + !clib_memcmp (sa->i_nonce, ikep->payload, plen - sizeof (*ikep))) + { + /* req is retransmit */ + if (sa->state == IKEV2_STATE_SA_INIT) + { + tmp = (ike_header_t *) sa->last_sa_init_res_packet_data; + u32 slen = clib_net_to_host_u32 (tmp->length); + ike->ispi = tmp->ispi; + ike->rspi = tmp->rspi; + ike->nextpayload = tmp->nextpayload; + ike->version = tmp->version; + ike->exchange = tmp->exchange; + ike->flags = tmp->flags; + ike->msgid = tmp->msgid; + ike->length = tmp->length; + clib_memcpy_fast (ike->payload, tmp->payload, + slen - sizeof (*ike)); + ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, + "ispi %lx IKE_SA_INIT retransmit " + "from %d.%d.%d.%d to %d.%d.%d.%d", + ike->ispi, + ip_addr_v4 (&raddr).as_u32, + ip_addr_v4 (&iaddr).as_u32); + return slen; + } + /* else ignore req */ + else + { + ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, + "ispi %lx IKE_SA_INIT ignore " + "from %d.%d.%d.%d to %d.%d.%d.%d", + ike->ispi, + ip_addr_v4 (&raddr).as_u32, + ip_addr_v4 (&iaddr).as_u32); + return ~0; + } + } + payload = ikep->nextpayload; + p += plen; + } + + return 0; +} + +static u32 +ikev2_retransmit_sa_init (ike_header_t * ike, ip_address_t iaddr, + ip_address_t raddr, u32 rlen) +{ + ikev2_sa_t *sa; + u32 res; + ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data (); + + /* *INDENT-OFF* */ + pool_foreach (sa, ptd->sas, ({ + res = ikev2_retransmit_sa_init_one (sa, ike, iaddr, raddr, rlen); + if (res) + return res; })); /* *INDENT-ON* */ @@ -2455,9 +2690,12 @@ ikev2_retransmit_sa_init (ike_header_t * ike, } static u32 -ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike, u32 rlen) +ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike) { - u32 msg_id = clib_net_to_host_u32 (ike->msgid); + if (ike_hdr_is_response (ike)) + return 0; + + u32 msg_id = clib_net_to_host_u32 (ike->msgid); /* new req */ if (msg_id > sa->last_msg_id) @@ -2481,13 +2719,15 @@ ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike, u32 rlen) ike->length = tmp->length; clib_memcpy_fast (ike->payload, tmp->payload, slen - sizeof (*ike)); ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, "IKE retransmit msgid %d", - msg_id, sa->raddr.as_u32, sa->iaddr.as_u32); + msg_id, ip_addr_v4 (&sa->raddr).as_u32, + ip_addr_v4 (&sa->iaddr).as_u32); return slen; } /* old req ignore */ ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, "IKE req ignore msgid %d", - msg_id, sa->raddr.as_u32, sa->iaddr.as_u32); + msg_id, ip_addr_v4 (&sa->raddr).as_u32, + ip_addr_v4 (&sa->iaddr).as_u32); return ~0; } @@ -2520,198 +2760,236 @@ ikev2_del_sa_init (u64 ispi) sizeof (ispi)); } -static uword -ikev2_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) +static_always_inline void +ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih) { - u32 n_left_from, *from, *to_next; - ikev2_next_t next_index; - ikev2_main_t *km = &ikev2_main; - u32 thread_index = vlib_get_thread_index (); + if (sa->is_initiator) + { + ip_address_copy_addr (&ih->dst_address, &sa->raddr); + ip_address_copy_addr (&ih->src_address, &sa->iaddr); + } + else + { + ip_address_copy_addr (&ih->dst_address, &sa->iaddr); + ip_address_copy_addr (&ih->src_address, &sa->raddr); + } +} - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; +static_always_inline void +ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih) +{ + if (sa->is_initiator) + { + ip_address_copy_addr (&ih->dst_address, &sa->raddr); + ip_address_copy_addr (&ih->src_address, &sa->iaddr); + } + else + { + ip_address_copy_addr (&ih->dst_address, &sa->iaddr); + ip_address_copy_addr (&ih->src_address, &sa->raddr); + } +} - while (n_left_from > 0) +static_always_inline void +ikev2_set_ip_address (ikev2_sa_t * sa, const void *iaddr, + const void *raddr, const int af) +{ + ip_address_set (&sa->raddr, raddr, af); + ip_address_set (&sa->iaddr, iaddr, af); +} + +static void +ikev2_elog_uint_peers_addr (u32 exchange, ip4_header_t * ip4, + ip6_header_t * ip6, u8 is_ip4) +{ + u32 src, dst; + if (is_ip4) + { + src = ip4->src_address.as_u32; + dst = ip4->dst_address.as_u32; + } + else { - u32 n_left_to_next; + src = ip6->src_address.as_u32[3]; + dst = ip6->dst_address.as_u32[3]; + } + ikev2_elog_uint_peers (IKEV2_LOG_WARNING, "IKEv2 exchange %d " + "received from %d.%d.%d.%d to %d.%d.%d.%d", + exchange, src, dst); +} - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); +static void +ikev2_generate_sa_init_data_and_log (ikev2_sa_t * sa) +{ + ikev2_generate_sa_error_t rc = ikev2_generate_sa_init_data (sa); - while (n_left_from > 0 && n_left_to_next > 0) + if (PREDICT_TRUE (rc == IKEV2_GENERATE_SA_INIT_OK)) + return; + + if (rc == IKEV2_GENERATE_SA_INIT_ERR_NO_DH) + ikev2_elog_error (IKEV2_GENERATE_SA_INIT_OK_ERR_NO_DH_STR); + else if (rc == IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH) + ikev2_elog_error (IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR); +} + +static_always_inline uword +ikev2_node_internal (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame, + u8 is_ip4) +{ + u32 n_left = frame->n_vectors, *from; + ikev2_main_t *km = &ikev2_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data (); + int res; + + from = vlib_frame_vector_args (frame); + vlib_get_buffers (vm, from, bufs, n_left); + b = bufs; + + while (n_left > 0) + { + vlib_buffer_t *b0 = b[0]; + next[0] = is_ip4 ? IKEV2_NEXT_IP4_ERROR_DROP + : IKEV2_NEXT_IP6_ERROR_DROP; + ip4_header_t *ip40 = 0; + ip6_header_t *ip60 = 0; + udp_header_t *udp0; + ike_header_t *ike0; + ikev2_sa_t *sa0 = 0; + ikev2_sa_t sa; /* temporary store for SA */ + u32 rlen, slen = 0; + int ip_hdr_sz = 0; + int is_req = 0, has_non_esp_marker = 0; + + ASSERT (0 == b0->punt_reason + || (is_ip4 + && b0->punt_reason == + ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0])); + + if (is_ip4 + && b0->punt_reason == ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0]) { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = IKEV2_NEXT_ERROR_DROP; - u32 sw_if_index0; - ip4_header_t *ip40; - udp_header_t *udp0; - ike_header_t *ike0; - ikev2_sa_t *sa0 = 0; - ikev2_sa_t sa; /* temporary store for SA */ - u32 rlen, slen = 0; - int is_req = 0, has_non_esp_marker = 0; - - /* 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 (b0->punt_reason == ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0]) + u8 *ptr = vlib_buffer_get_current (b0); + ip40 = (ip4_header_t *) ptr; + ptr += sizeof (*ip40); + udp0 = (udp_header_t *) ptr; + ptr += sizeof (*udp0); + ike0 = (ike_header_t *) ptr; + ip_hdr_sz = sizeof (*ip40); + } + else + { + u8 *ipx_hdr = b0->data + vnet_buffer (b0)->l3_hdr_offset; + ike0 = vlib_buffer_get_current (b0); + vlib_buffer_advance (b0, -sizeof (*udp0)); + udp0 = vlib_buffer_get_current (b0); + + if (is_ip4) { - u8 *ptr = vlib_buffer_get_current (b0); - ip40 = (ip4_header_t *) ptr; - ptr += sizeof (*ip40); - udp0 = (udp_header_t *) ptr; - ptr += sizeof (*udp0); - ike0 = (ike_header_t *) ptr; + ip40 = (ip4_header_t *) ipx_hdr; + ip_hdr_sz = sizeof (*ip40); } else { - ike0 = vlib_buffer_get_current (b0); - vlib_buffer_advance (b0, -sizeof (*udp0)); - udp0 = vlib_buffer_get_current (b0); - vlib_buffer_advance (b0, -sizeof (*ip40)); - ip40 = vlib_buffer_get_current (b0); + ip60 = (ip6_header_t *) ipx_hdr; + ip_hdr_sz = sizeof (*ip60); } + vlib_buffer_advance (b0, -ip_hdr_sz); + } - rlen = b0->current_length - sizeof (*ip40) - sizeof (*udp0); + rlen = b0->current_length - ip_hdr_sz - sizeof (*udp0); - /* check for non-esp marker */ - if (*((u32 *) ike0) == 0) - { - ike0 = - (ike_header_t *) ((u8 *) ike0 + - sizeof (ikev2_non_esp_marker)); - rlen -= sizeof (ikev2_non_esp_marker); - has_non_esp_marker = 1; - } + /* check for non-esp marker */ + if (*((u32 *) ike0) == 0) + { + ike0 = + (ike_header_t *) ((u8 *) ike0 + sizeof (ikev2_non_esp_marker)); + rlen -= sizeof (ikev2_non_esp_marker); + has_non_esp_marker = 1; + } - if (clib_net_to_host_u32 (ike0->length) != rlen) - { - vlib_node_increment_counter (vm, ikev2_node.index, - IKEV2_ERROR_BAD_LENGTH, 1); - goto dispatch0; - } + if (clib_net_to_host_u32 (ike0->length) != rlen) + { + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_BAD_LENGTH, 1); + goto dispatch0; + } - if (ike0->version != IKE_VERSION_2) - { - vlib_node_increment_counter (vm, ikev2_node.index, - IKEV2_ERROR_NOT_IKEV2, 1); - goto dispatch0; - } + if (ike0->version != IKE_VERSION_2) + { + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_NOT_IKEV2, 1); + goto dispatch0; + } - if (ike0->exchange == IKEV2_EXCHANGE_SA_INIT) - { - sa0 = &sa; - clib_memset (sa0, 0, sizeof (*sa0)); + if (ike0->exchange == IKEV2_EXCHANGE_SA_INIT) + { + sa0 = &sa; + clib_memset (sa0, 0, sizeof (*sa0)); - if (ike0->flags & IKEV2_HDR_FLAG_INITIATOR) + if (ike_hdr_is_initiator (ike0)) + { + if (ike0->rspi == 0) { - if (ike0->rspi == 0) - { - sa0->raddr.as_u32 = ip40->dst_address.as_u32; - sa0->iaddr.as_u32 = ip40->src_address.as_u32; - sa0->dst_port = clib_net_to_host_u16 (udp0->src_port); - - slen = - ikev2_retransmit_sa_init (ike0, sa0->iaddr, - sa0->raddr, rlen); - if (slen) - { - vlib_node_increment_counter (vm, ikev2_node.index, - ~0 == - slen ? - IKEV2_ERROR_IKE_SA_INIT_IGNORE - : - IKEV2_ERROR_IKE_SA_INIT_RETRANSMIT, - 1); - goto dispatch0; - } + if (is_ip4) + ikev2_set_ip_address (sa0, &ip40->src_address, + &ip40->dst_address, AF_IP4); + else + ikev2_set_ip_address (sa0, &ip60->src_address, + &ip60->dst_address, AF_IP6); - ikev2_process_sa_init_req (vm, sa0, ike0, udp0, rlen); + sa0->dst_port = clib_net_to_host_u16 (udp0->src_port); - if (sa0->state == IKEV2_STATE_SA_INIT) - { - ikev2_sa_free_proposal_vector (&sa0->r_proposals); - sa0->r_proposals = - ikev2_select_proposal (sa0->i_proposals, - IKEV2_PROTOCOL_IKE); - ikev2_generate_sa_init_data (sa0); - } + slen = + ikev2_retransmit_sa_init (ike0, sa0->iaddr, + sa0->raddr, rlen); + if (slen) + { + vlib_node_increment_counter (vm, node->node_index, + ~0 == + slen ? + IKEV2_ERROR_IKE_SA_INIT_IGNORE + : + IKEV2_ERROR_IKE_SA_INIT_RETRANSMIT, + 1); + goto dispatch0; + } - if (sa0->state == IKEV2_STATE_SA_INIT - || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE) - { - slen = ikev2_generate_message (sa0, ike0, 0, udp0); - } + res = ikev2_process_sa_init_req (vm, sa0, ike0, udp0, rlen); + if (!res) + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_MALFORMED_PACKET, + 1); - if (sa0->state == IKEV2_STATE_SA_INIT) - { - /* add SA to the pool */ - pool_get (km->per_thread_data[thread_index].sas, - sa0); - clib_memcpy_fast (sa0, &sa, sizeof (*sa0)); - ikev2_init_sa (vm, sa0); - hash_set (km-> - per_thread_data[thread_index].sa_by_rspi, - sa0->rspi, - sa0 - - km->per_thread_data[thread_index].sas); - } - else - { - ikev2_sa_free_all_vec (sa0); - } + if (sa0->state == IKEV2_STATE_SA_INIT) + { + ikev2_sa_free_proposal_vector (&sa0->r_proposals); + sa0->r_proposals = + ikev2_select_proposal (sa0->i_proposals, + IKEV2_PROTOCOL_IKE); + ikev2_generate_sa_init_data_and_log (sa0); } - } - else //received sa_init without initiator flag - { - sa0->raddr.as_u32 = ip40->src_address.as_u32; - sa0->iaddr.as_u32 = ip40->dst_address.as_u32; - ikev2_process_sa_init_resp (vm, sa0, ike0, udp0, rlen); - if (sa0->state == IKEV2_STATE_SA_INIT) + if (sa0->state == IKEV2_STATE_SA_INIT + || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE) { - is_req = 1; - ike0->exchange = IKEV2_EXCHANGE_IKE_AUTH; - uword *p = hash_get (km->sa_by_ispi, ike0->ispi); - if (p) - { - ikev2_sa_t *sai = - pool_elt_at_index (km->sais, p[0]); - - if (clib_atomic_bool_cmp_and_swap - (&sai->init_response_received, 0, 1)) - { - ikev2_complete_sa_data (sa0, sai); - ikev2_calc_keys (sa0); - ikev2_sa_auth_init (sa0); - slen = - ikev2_generate_message (sa0, ike0, 0, udp0); - } - else - { - /* we've already processed sa-init response */ - sa0->state = IKEV2_STATE_UNKNOWN; - } - } + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + if (~0 == slen) + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_NO_BUFF_SPACE, + 1); } if (sa0->state == IKEV2_STATE_SA_INIT) { /* add SA to the pool */ - pool_get (km->per_thread_data[thread_index].sas, sa0); + pool_get (ptd->sas, sa0); clib_memcpy_fast (sa0, &sa, sizeof (*sa0)); - hash_set (km->per_thread_data[thread_index].sa_by_rspi, - sa0->rspi, - sa0 - km->per_thread_data[thread_index].sas); + ikev2_init_sa (vm, sa0); + hash_set (ptd->sa_by_rspi, sa0->rspi, sa0 - ptd->sas); } else { @@ -2719,262 +2997,353 @@ ikev2_node_fn (vlib_main_t * vm, } } } - else if (ike0->exchange == IKEV2_EXCHANGE_IKE_AUTH) + else //received sa_init without initiator flag { - uword *p; - p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, - clib_net_to_host_u64 (ike0->rspi)); - if (p) - { - sa0 = - pool_elt_at_index (km->per_thread_data[thread_index].sas, - p[0]); + if (is_ip4) + ikev2_set_ip_address (sa0, &ip40->dst_address, + &ip40->src_address, AF_IP4); + else + ikev2_set_ip_address (sa0, &ip60->dst_address, + &ip60->src_address, AF_IP6); - slen = ikev2_retransmit_resp (sa0, ike0, rlen); - if (slen) - { - vlib_node_increment_counter (vm, ikev2_node.index, - ~0 == - slen ? - IKEV2_ERROR_IKE_REQ_IGNORE - : - IKEV2_ERROR_IKE_REQ_RETRANSMIT, - 1); - goto dispatch0; - } + ikev2_process_sa_init_resp (vm, sa0, ike0, udp0, rlen); - sa0->dst_port = clib_net_to_host_u16 (udp0->src_port); - ikev2_process_auth_req (vm, sa0, ike0, rlen); - ikev2_sa_auth (sa0); - if (sa0->state == IKEV2_STATE_AUTHENTICATED) + if (sa0->state == IKEV2_STATE_SA_INIT) + { + is_req = 1; + ike0->exchange = IKEV2_EXCHANGE_IKE_AUTH; + uword *p = hash_get (km->sa_by_ispi, sa0->ispi); + if (p) { - ikev2_initial_contact_cleanup (sa0); - ikev2_sa_match_ts (sa0); - if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE) - ikev2_create_tunnel_interface (vm, thread_index, sa0, - &sa0->childs[0], - p[0], 0, 0); - } + ikev2_sa_t *sai = pool_elt_at_index (km->sais, p[0]); - if (sa0->is_initiator) - { - ikev2_del_sa_init (ike0->ispi); - } - else - { - slen = ikev2_generate_message (sa0, ike0, 0, udp0); + if (clib_atomic_bool_cmp_and_swap + (&sai->init_response_received, 0, 1)) + { + ikev2_complete_sa_data (sa0, sai); + ikev2_calc_keys (sa0); + ikev2_sa_auth_init (sa0); + ike0->flags = IKEV2_HDR_FLAG_INITIATOR; + ike0->msgid = + clib_net_to_host_u32 (sai->last_init_msg_id); + sa0->last_init_msg_id = sai->last_init_msg_id + 1; + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0); + if (~0 == slen) + vlib_node_increment_counter (vm, + node->node_index, + IKEV2_ERROR_NO_BUFF_SPACE, + 1); + } + else + { + /* we've already processed sa-init response */ + sa0->state = IKEV2_STATE_UNKNOWN; + } } } + + if (sa0->state == IKEV2_STATE_SA_INIT) + { + /* add SA to the pool */ + pool_get (ptd->sas, sa0); + clib_memcpy_fast (sa0, &sa, sizeof (*sa0)); + hash_set (ptd->sa_by_rspi, sa0->rspi, sa0 - ptd->sas); + } + else + { + ikev2_sa_free_all_vec (sa0); + } } - else if (ike0->exchange == IKEV2_EXCHANGE_INFORMATIONAL) + } + else if (ike0->exchange == IKEV2_EXCHANGE_IKE_AUTH) + { + uword *p; + p = hash_get (ptd->sa_by_rspi, clib_net_to_host_u64 (ike0->rspi)); + if (p) { - uword *p; - p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, - clib_net_to_host_u64 (ike0->rspi)); - if (p) + sa0 = pool_elt_at_index (ptd->sas, p[0]); + slen = ikev2_retransmit_resp (sa0, ike0); + if (slen) { - sa0 = - pool_elt_at_index (km->per_thread_data[thread_index].sas, - p[0]); + vlib_node_increment_counter (vm, node->node_index, + ~0 == + slen ? + IKEV2_ERROR_IKE_REQ_IGNORE + : + IKEV2_ERROR_IKE_REQ_RETRANSMIT, + 1); + goto dispatch0; + } - slen = ikev2_retransmit_resp (sa0, ike0, rlen); - if (slen) - { - vlib_node_increment_counter (vm, ikev2_node.index, - ~0 == - slen ? - IKEV2_ERROR_IKE_REQ_IGNORE - : - IKEV2_ERROR_IKE_REQ_RETRANSMIT, - 1); - goto dispatch0; - } + sa0->dst_port = clib_net_to_host_u16 (udp0->src_port); + res = ikev2_process_auth_req (vm, sa0, ike0, rlen); + if (res) + ikev2_sa_auth (sa0); + else + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_MALFORMED_PACKET, 1); + if (sa0->state == IKEV2_STATE_AUTHENTICATED) + { + ikev2_initial_contact_cleanup (ptd, sa0); + ikev2_sa_match_ts (sa0); + if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE) + ikev2_create_tunnel_interface (vm, sa0, + &sa0->childs[0], + p[0], 0, 0); + } - ikev2_process_informational_req (vm, sa0, ike0, rlen); - if (sa0->del) + if (sa0->is_initiator) + { + ikev2_del_sa_init (sa0->ispi); + } + else + { + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + if (~0 == slen) + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_NO_BUFF_SPACE, + 1); + } + } + } + else if (ike0->exchange == IKEV2_EXCHANGE_INFORMATIONAL) + { + uword *p; + p = hash_get (ptd->sa_by_rspi, clib_net_to_host_u64 (ike0->rspi)); + if (p) + { + sa0 = pool_elt_at_index (ptd->sas, p[0]); + slen = ikev2_retransmit_resp (sa0, ike0); + if (slen) + { + vlib_node_increment_counter (vm, node->node_index, + ~0 == + slen ? + IKEV2_ERROR_IKE_REQ_IGNORE + : + IKEV2_ERROR_IKE_REQ_RETRANSMIT, + 1); + goto dispatch0; + } + + res = ikev2_process_informational_req (vm, sa0, ike0, rlen); + if (!res) + { + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_MALFORMED_PACKET, + 1); + slen = ~0; + goto dispatch0; + } + + if (sa0->del) + { + if (sa0->del[0].protocol_id != IKEV2_PROTOCOL_IKE) { - if (sa0->del[0].protocol_id != IKEV2_PROTOCOL_IKE) - { - ikev2_delete_t *d, *tmp, *resp = 0; - vec_foreach (d, sa0->del) + ikev2_delete_t *d, *tmp, *resp = 0; + vec_foreach (d, sa0->del) + { + ikev2_child_sa_t *ch_sa; + ch_sa = ikev2_sa_get_child (sa0, d->spi, + d->protocol_id, + !sa0->is_initiator); + if (ch_sa) { - ikev2_child_sa_t *ch_sa; - ch_sa = ikev2_sa_get_child (sa0, d->spi, - d->protocol_id, - !sa0->is_initiator); - if (ch_sa) + ikev2_delete_tunnel_interface (km->vnet_main, + sa0, ch_sa); + if (!sa0->is_initiator) { - ikev2_delete_tunnel_interface (km->vnet_main, - sa0, ch_sa); - if (!sa0->is_initiator) - { - vec_add2 (resp, tmp, 1); - tmp->protocol_id = d->protocol_id; - tmp->spi = ch_sa->r_proposals[0].spi; - } - ikev2_sa_del_child_sa (sa0, ch_sa); + vec_add2 (resp, tmp, 1); + tmp->protocol_id = d->protocol_id; + tmp->spi = ch_sa->r_proposals[0].spi; } + ikev2_sa_del_child_sa (sa0, ch_sa); } - if (!sa0->is_initiator) - { - vec_free (sa0->del); - sa0->del = resp; - } + } + if (!sa0->is_initiator) + { + vec_free (sa0->del); + sa0->del = resp; } } - if (!(ike0->flags & IKEV2_HDR_FLAG_RESPONSE)) - { - ike0->flags |= IKEV2_HDR_FLAG_RESPONSE; - slen = ikev2_generate_message (sa0, ike0, 0, udp0); - } + } + if (ike_hdr_is_request (ike0)) + { + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + if (~0 == slen) + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_NO_BUFF_SPACE, + 1); } } - else if (ike0->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA) + } + else if (ike0->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA) + { + uword *p; + p = hash_get (ptd->sa_by_rspi, clib_net_to_host_u64 (ike0->rspi)); + if (p) { - uword *p; - p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, - clib_net_to_host_u64 (ike0->rspi)); - if (p) + sa0 = pool_elt_at_index (ptd->sas, p[0]); + slen = ikev2_retransmit_resp (sa0, ike0); + if (slen) { - sa0 = - pool_elt_at_index (km->per_thread_data[thread_index].sas, - p[0]); + vlib_node_increment_counter (vm, node->node_index, + ~0 == + slen ? + IKEV2_ERROR_IKE_REQ_IGNORE + : + IKEV2_ERROR_IKE_REQ_RETRANSMIT, + 1); + goto dispatch0; + } - slen = ikev2_retransmit_resp (sa0, ike0, rlen); - if (slen) + res = ikev2_process_create_child_sa_req (vm, sa0, ike0, rlen); + if (!res) + { + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_MALFORMED_PACKET, + 1); + slen = ~0; + goto dispatch0; + } + + if (sa0->rekey) + { + if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE) { - vlib_node_increment_counter (vm, ikev2_node.index, - ~0 == - slen ? - IKEV2_ERROR_IKE_REQ_IGNORE - : - IKEV2_ERROR_IKE_REQ_RETRANSMIT, - 1); - goto dispatch0; + if (sa0->childs) + ikev2_sa_free_all_child_sa (&sa0->childs); + ikev2_child_sa_t *child; + vec_add2 (sa0->childs, child, 1); + clib_memset (child, 0, sizeof (*child)); + child->r_proposals = sa0->rekey[0].r_proposal; + child->i_proposals = sa0->rekey[0].i_proposal; + child->tsi = sa0->rekey[0].tsi; + child->tsr = sa0->rekey[0].tsr; + ikev2_create_tunnel_interface (vm, sa0, child, p[0], + child - sa0->childs, 1); } - - ikev2_process_create_child_sa_req (vm, sa0, ike0, rlen); - if (sa0->rekey) + if (ike_hdr_is_response (ike0)) { - if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE) - { - if (sa0->childs) - ikev2_sa_free_all_child_sa (&sa0->childs); - ikev2_child_sa_t *child; - vec_add2 (sa0->childs, child, 1); - clib_memset (child, 0, sizeof (*child)); - child->r_proposals = sa0->rekey[0].r_proposal; - child->i_proposals = sa0->rekey[0].i_proposal; - child->tsi = sa0->rekey[0].tsi; - child->tsr = sa0->rekey[0].tsr; - ikev2_create_tunnel_interface (vm, thread_index, - sa0, child, p[0], - child - sa0->childs, - 1); - } - if (sa0->is_initiator) - { - vec_free (sa0->rekey); - } - else - { - slen = ikev2_generate_message (sa0, ike0, 0, udp0); - } + vec_free (sa0->rekey); + } + else + { + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + if (~0 == slen) + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_NO_BUFF_SPACE, + 1); } } } + } + else + { + ikev2_elog_uint_peers_addr (ike0->exchange, ip40, ip60, is_ip4); + } + + dispatch0: + /* if we are sending packet back, rewrite headers */ + if (slen && ~0 != slen) + { + if (is_ip4) + { + next[0] = IKEV2_NEXT_IP4_LOOKUP; + ikev2_rewrite_v4_addrs (sa0, ip40); + } else { - ikev2_elog_uint_peers (IKEV2_LOG_WARNING, "IKEv2 exchange %d " - "received from %d.%d.%d.%d to %d.%d.%d.%d", - ike0->exchange, - ip40->src_address.as_u32, - ip40->dst_address.as_u32); + next[0] = IKEV2_NEXT_IP6_LOOKUP; + ikev2_rewrite_v6_addrs (sa0, ip60); } - dispatch0: - /* if we are sending packet back, rewrite headers */ - if (slen && ~0 != slen) + if (is_req) { - next0 = IKEV2_NEXT_IP4_LOOKUP; - if (sa0->is_initiator) - { - ip40->dst_address.as_u32 = sa0->raddr.as_u32; - ip40->src_address.as_u32 = sa0->iaddr.as_u32; - } - else - { - ip40->dst_address.as_u32 = sa0->iaddr.as_u32; - ip40->src_address.as_u32 = sa0->raddr.as_u32; - } + udp0->dst_port = udp0->src_port = + clib_net_to_host_u16 (ikev2_get_port (sa0)); - if (is_req) + if (udp0->dst_port == clib_net_to_host_u16 (IKEV2_PORT_NATT) + && ikev2_natt_active (sa0)) { - udp0->dst_port = udp0->src_port = - clib_net_to_host_u16 (ikev2_get_port (sa0)); - - if (udp0->dst_port == clib_net_to_host_u16 (IKEV2_PORT_NATT) - && sa0->natt) - { - if (!has_non_esp_marker) - slen = ikev2_insert_non_esp_marker (ike0, slen); - } + if (!has_non_esp_marker) + slen = ikev2_insert_non_esp_marker (ike0, slen); } - else - { - if (has_non_esp_marker) - slen += sizeof (ikev2_non_esp_marker); + } + else + { + if (has_non_esp_marker) + slen += sizeof (ikev2_non_esp_marker); - u16 tp = udp0->dst_port; - udp0->dst_port = udp0->src_port; - udp0->src_port = tp; - } + u16 tp = udp0->dst_port; + udp0->dst_port = udp0->src_port; + udp0->src_port = tp; + } - udp0->length = - clib_host_to_net_u16 (slen + sizeof (udp_header_t)); - udp0->checksum = 0; - b0->current_length = - slen + sizeof (ip4_header_t) + sizeof (udp_header_t); + udp0->length = clib_host_to_net_u16 (slen + sizeof (udp_header_t)); + udp0->checksum = 0; + b0->current_length = slen + ip_hdr_sz + sizeof (udp_header_t); + if (is_ip4) + { ip40->length = clib_host_to_net_u16 (b0->current_length); ip40->checksum = ip4_header_checksum (ip40); } - /* delete sa */ - if (sa0 && (sa0->state == IKEV2_STATE_DELETED || - sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE)) + else { - ikev2_child_sa_t *c; - - vec_foreach (c, sa0->childs) - ikev2_delete_tunnel_interface (km->vnet_main, sa0, c); - - ikev2_delete_sa (sa0); + ip60->payload_length = + clib_host_to_net_u16 (b0->current_length - sizeof (*ip60)); } - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + } + /* delete sa */ + if (sa0 && (sa0->state == IKEV2_STATE_DELETED || + sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE)) + { + ikev2_child_sa_t *c; - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - ikev2_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - } + vec_foreach (c, sa0->childs) + ikev2_delete_tunnel_interface (km->vnet_main, sa0, c); - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, bi0, next0); + ikev2_delete_sa (ptd, sa0); } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { - vlib_put_next_frame (vm, node, next_index, n_left_to_next); + ikev2_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next[0]; + } + n_left -= 1; + next += 1; + b += 1; } - vlib_node_increment_counter (vm, ikev2_node.index, + vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_PROCESSED, frame->n_vectors); + vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); return frame->n_vectors; } +static uword +ikev2_ip4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */ ); +} + +static uword +ikev2_ip6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return ikev2_node_internal (vm, node, frame, 0 /* is_ip4 */ ); +} + /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ikev2_node,static) = { - .function = ikev2_node_fn, - .name = "ikev2", +VLIB_REGISTER_NODE (ikev2_node_ip4,static) = { + .function = ikev2_ip4, + .name = "ikev2-ip4", .vector_size = sizeof (u32), .format_trace = format_ikev2_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -2982,11 +3351,27 @@ VLIB_REGISTER_NODE (ikev2_node,static) = { .n_errors = ARRAY_LEN(ikev2_error_strings), .error_strings = ikev2_error_strings, - .n_next_nodes = IKEV2_N_NEXT, - + .n_next_nodes = IKEV2_IP4_N_NEXT, .next_nodes = { [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup", - [IKEV2_NEXT_ERROR_DROP] = "error-drop", + [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (ikev2_node_ip6,static) = { + .function = ikev2_ip6, + .name = "ikev2-ip6", + .vector_size = sizeof (u32), + .format_trace = format_ikev2_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(ikev2_error_strings), + .error_strings = ikev2_error_strings, + + .n_next_nodes = IKEV2_IP6_N_NEXT, + .next_nodes = { + [IKEV2_NEXT_IP6_LOOKUP] = "ip6-lookup", + [IKEV2_NEXT_IP6_ERROR_DROP] = "error-drop", }, }; /* *INDENT-ON* */ @@ -3134,10 +3519,11 @@ ikev2_profile_index_by_name (u8 * name) static void -ikev2_send_ike (vlib_main_t * vm, ip4_address_t * src, ip4_address_t * dst, +ikev2_send_ike (vlib_main_t * vm, ip_address_t * src, ip_address_t * dst, u32 bi0, u32 len, u16 src_port, u16 dst_port, u32 sw_if_index) { ip4_header_t *ip40; + ip6_header_t *ip60; udp_header_t *udp0; vlib_buffer_t *b0; vlib_frame_t *f; @@ -3146,49 +3532,72 @@ ikev2_send_ike (vlib_main_t * vm, ip4_address_t * src, ip4_address_t * dst, b0 = vlib_get_buffer (vm, bi0); vlib_buffer_advance (b0, -sizeof (udp_header_t)); udp0 = vlib_buffer_get_current (b0); - vlib_buffer_advance (b0, -sizeof (ip4_header_t)); - ip40 = vlib_buffer_get_current (b0); - - - ip40->ip_version_and_header_length = 0x45; - ip40->tos = 0; - ip40->fragment_id = 0; - ip40->flags_and_fragment_offset = 0; - ip40->ttl = 0xff; - ip40->protocol = IP_PROTOCOL_UDP; - ip40->dst_address.as_u32 = dst->as_u32; - ip40->src_address.as_u32 = src->as_u32; udp0->dst_port = clib_host_to_net_u16 (dst_port); udp0->src_port = clib_host_to_net_u16 (src_port); udp0->length = clib_host_to_net_u16 (len + sizeof (udp_header_t)); udp0->checksum = 0; - b0->current_length = len + sizeof (ip4_header_t) + sizeof (udp_header_t); - ip40->length = clib_host_to_net_u16 (b0->current_length); - ip40->checksum = ip4_header_checksum (ip40); + if (ip_addr_version (dst) == AF_IP4) + { + vlib_buffer_advance (b0, -sizeof (ip4_header_t)); + ip40 = vlib_buffer_get_current (b0); + ip40->ip_version_and_header_length = 0x45; + ip40->tos = 0; + ip40->fragment_id = 0; + ip40->flags_and_fragment_offset = 0; + ip40->ttl = 0xff; + ip40->protocol = IP_PROTOCOL_UDP; + ip40->dst_address.as_u32 = ip_addr_v4 (dst).as_u32; + ip40->src_address.as_u32 = ip_addr_v4 (src).as_u32; + b0->current_length = + len + sizeof (ip4_header_t) + sizeof (udp_header_t); + ip40->length = clib_host_to_net_u16 (b0->current_length); + ip40->checksum = ip4_header_checksum (ip40); + } + else + { + vlib_buffer_advance (b0, -sizeof (ip6_header_t)); + ip60 = vlib_buffer_get_current (b0); + + b0->current_length = len + sizeof (*ip60) + sizeof (udp_header_t); + ip60->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (0x6 << 28); + ip60->payload_length = + clib_host_to_net_u16 (b0->current_length - sizeof (*ip60)); + ip60->protocol = IP_PROTOCOL_UDP; + ip60->hop_limit = 0xff; + clib_memcpy_fast (ip60->src_address.as_u8, ip_addr_v6 (src).as_u8, + sizeof (ip60->src_address)); + clib_memcpy_fast (ip60->dst_address.as_u8, ip_addr_v6 (dst).as_u8, + sizeof (ip60->src_address)); + } + + b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; vnet_buffer (b0)->sw_if_index[VLIB_RX] = sw_if_index; vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0; + u32 next_index = (ip_addr_version (dst) == AF_IP4) ? + ip4_lookup_node.index : ip6_lookup_node.index; + /* send the request */ - f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + f = vlib_get_frame_to_node (vm, next_index); to_next = vlib_frame_vector_args (f); to_next[0] = bi0; f->n_vectors = 1; - vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); + vlib_put_frame_to_node (vm, next_index, f); } static u32 -ikev2_get_new_ike_header_buff (vlib_main_t * vm, ike_header_t ** ike) +ikev2_get_new_ike_header_buff (vlib_main_t * vm, vlib_buffer_t ** b) { u32 bi0; if (vlib_buffer_alloc (vm, &bi0, 1) != 1) { - *ike = 0; + *b = 0; return 0; } - vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); - *ike = vlib_buffer_get_current (b0); + *b = vlib_get_buffer (vm, bi0); return bi0; } @@ -3197,6 +3606,8 @@ ikev2_set_local_key (vlib_main_t * vm, u8 * file) { ikev2_main_t *km = &ikev2_main; + if (km->pkey) + EVP_PKEY_free (km->pkey); km->pkey = ikev2_load_key_file (file); if (km->pkey == NULL) return clib_error_return (0, "load key '%s' failed", file); @@ -3259,49 +3670,62 @@ ikev2_unregister_udp_port (ikev2_profile_t * p) static void ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm, ikev2_main_per_thread_data_t * tkm, - ikev2_sa_t * sa) + ikev2_sa_t * sa, u8 send_notification) { ikev2_main_t *km = &ikev2_main; - ip4_address_t *src, *dst; + ip_address_t *src, *dst; + vlib_buffer_t *b0; + ikev2_child_sa_t *c; /* Create the Initiator notification for IKE SA removal */ ike_header_t *ike0; u32 bi0 = 0; int len; - bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); - if (!bi0) - { - ikev2_log_error ("buffer alloc failure"); - return; - } - - ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL; - ike0->ispi = clib_host_to_net_u64 (sa->ispi); - ike0->rspi = clib_host_to_net_u64 (sa->rspi); vec_resize (sa->del, 1); sa->del->protocol_id = IKEV2_PROTOCOL_IKE; sa->del->spi = sa->ispi; - ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1); - sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); - len = ikev2_generate_message (sa, ike0, 0, 0); - if (sa->is_initiator) - { - src = &sa->iaddr; - dst = &sa->raddr; - } - else + if (send_notification) { - dst = &sa->iaddr; - src = &sa->raddr; - } + bi0 = ikev2_get_new_ike_header_buff (vm, &b0); + if (!bi0) + { + ikev2_log_error ("buffer alloc failure"); + goto delete_sa; + } - ikev2_send_ike (vm, src, dst, bi0, len, - ikev2_get_port (sa), sa->dst_port, 0); + ike0 = vlib_buffer_get_current (b0); + ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL; + ike0->ispi = clib_host_to_net_u64 (sa->ispi); + ike0->rspi = clib_host_to_net_u64 (sa->rspi); + ike0->flags = 0; + ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); + sa->last_init_msg_id += 1; + len = ikev2_generate_message (b0, sa, ike0, 0, 0); + if (~0 == len) + return; + + if (ikev2_natt_active (sa)) + len = ikev2_insert_non_esp_marker (ike0, len); + + if (sa->is_initiator) + { + src = &sa->iaddr; + dst = &sa->raddr; + } + else + { + dst = &sa->iaddr; + src = &sa->raddr; + } + + ikev2_send_ike (vm, src, dst, bi0, len, + ikev2_get_port (sa), sa->dst_port, 0); + } +delete_sa: /* delete local SA */ - ikev2_child_sa_t *c; vec_foreach (c, sa->childs) ikev2_delete_tunnel_interface (km->vnet_main, sa, c); @@ -3324,6 +3748,22 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p) u32 *sai; u32 *del_sai = 0; + /* *INDENT-OFF* */ + pool_foreach(sa, km->sais, ({ + if (pi == sa->profile_index) + vec_add1 (del_sai, sa - km->sais); + })); + /* *INDENT-ON* */ + + vec_foreach (sai, del_sai) + { + sa = pool_elt_at_index (km->sais, sai[0]); + ikev2_sa_free_all_vec (sa); + hash_unset (km->sa_by_ispi, sa->ispi); + pool_put (km->sais, sa); + } + vec_reset_length (del_sai); + vec_foreach (tkm, km->per_thread_data) { /* *INDENT-OFF* */ @@ -3336,7 +3776,7 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p) vec_foreach (sai, del_sai) { sa = pool_elt_at_index (tkm->sas, sai[0]); - ikev2_initiate_delete_ike_sa_internal (km->vlib_main, tkm, sa); + ikev2_initiate_delete_ike_sa_internal (km->vlib_main, tkm, sa, 1); } vec_reset_length (del_sai); @@ -3345,6 +3785,19 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p) vec_free (del_sai); } +static void +ikev2_profile_free (ikev2_profile_t * p) +{ + vec_free (p->name); + + vec_free (p->auth.data); + if (p->auth.key) + EVP_PKEY_free (p->auth.key); + + vec_free (p->loc_id.data); + vec_free (p->rem_id.data); +} + clib_error_t * ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) { @@ -3374,7 +3827,7 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) ikev2_unregister_udp_port (p); ikev2_cleanup_profile_sessions (km, p); - vec_free (p->name); + ikev2_profile_free (p); pool_put (km->profiles, p); mhash_unset (&km->profile_index_by_name, name, 0); } @@ -3395,7 +3848,11 @@ ikev2_set_profile_auth (vlib_main_t * vm, u8 * name, u8 auth_method, r = clib_error_return (0, "unknown profile %v", name); return r; } + + if (p->auth.key) + EVP_PKEY_free (p->auth.key); vec_free (p->auth.data); + p->auth.method = auth_method; p->auth.data = vec_dup (auth_data); p->auth.hex = data_hex_format; @@ -3403,8 +3860,6 @@ ikev2_set_profile_auth (vlib_main_t * vm, u8 * name, u8 auth_method, if (auth_method == IKEV2_AUTH_METHOD_RSA_SIG) { vec_add1 (p->auth.data, 0); - if (p->auth.key) - EVP_PKEY_free (p->auth.key); p->auth.key = ikev2_load_cert_file (p->auth.data); if (p->auth.key == NULL) return clib_error_return (0, "load cert '%s' failed", p->auth.data); @@ -3413,6 +3868,15 @@ ikev2_set_profile_auth (vlib_main_t * vm, u8 * name, u8 auth_method, return 0; } +static int +ikev2_is_id_supported (u8 id_type) +{ + return (id_type == IKEV2_ID_TYPE_ID_IPV4_ADDR || + id_type == IKEV2_ID_TYPE_ID_IPV6_ADDR || + id_type == IKEV2_ID_TYPE_ID_RFC822_ADDR || + id_type == IKEV2_ID_TYPE_ID_FQDN); +} + clib_error_t * ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data, int is_local) @@ -3420,8 +3884,7 @@ ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data, ikev2_profile_t *p; clib_error_t *r; - if (id_type > IKEV2_ID_TYPE_ID_RFC822_ADDR - && id_type < IKEV2_ID_TYPE_ID_KEY_ID) + if (!ikev2_is_id_supported (id_type)) { r = clib_error_return (0, "unsupported identity type %U", format_ikev2_id_type, id_type); @@ -3452,10 +3915,27 @@ ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data, return 0; } +static_always_inline void +ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr) +{ + if (ip_addr_version (addr) == AF_IP4) + ts->ts_type = TS_IPV4_ADDR_RANGE; + else + ts->ts_type = TS_IPV6_ADDR_RANGE; +} + +static_always_inline void +ikev2_set_ts_addrs (ikev2_ts_t * ts, const ip_address_t * start, + const ip_address_t * end) +{ + ip_address_copy (&ts->start_addr, start); + ip_address_copy (&ts->end_addr, end); +} + clib_error_t * ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id, - u16 start_port, u16 end_port, ip4_address_t start_addr, - ip4_address_t end_addr, int is_local) + u16 start_port, u16 end_port, ip_address_t start_addr, + ip_address_t end_addr, int is_local) { ikev2_profile_t *p; clib_error_t *r; @@ -3468,23 +3948,24 @@ ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id, return r; } + if (ip_addr_version (&start_addr) != ip_addr_version (&end_addr)) + return clib_error_return (0, "IP address version mismatch!"); + if (is_local) { - p->loc_ts.start_addr.as_u32 = start_addr.as_u32; - p->loc_ts.end_addr.as_u32 = end_addr.as_u32; + ikev2_set_ts_addrs (&p->loc_ts, &start_addr, &end_addr); p->loc_ts.start_port = start_port; p->loc_ts.end_port = end_port; p->loc_ts.protocol_id = protocol_id; - p->loc_ts.ts_type = 7; + ikev2_set_ts_type (&p->loc_ts, &start_addr); } else { - p->rem_ts.start_addr.as_u32 = start_addr.as_u32; - p->rem_ts.end_addr.as_u32 = end_addr.as_u32; + ikev2_set_ts_addrs (&p->rem_ts, &start_addr, &end_addr); p->rem_ts.start_port = start_port; p->rem_ts.end_port = end_port; p->rem_ts.protocol_id = protocol_id; - p->rem_ts.ts_type = 7; + ikev2_set_ts_type (&p->rem_ts, &start_addr); } return 0; @@ -3493,7 +3974,7 @@ ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id, clib_error_t * ikev2_set_profile_responder (vlib_main_t * vm, u8 * name, - u32 sw_if_index, ip4_address_t ip4) + u32 sw_if_index, ip_address_t addr) { ikev2_profile_t *p; clib_error_t *r; @@ -3507,7 +3988,7 @@ ikev2_set_profile_responder (vlib_main_t * vm, u8 * name, } p->responder.sw_if_index = sw_if_index; - p->responder.ip4 = ip4; + ip_address_copy (&p->responder.addr, &addr); return 0; } @@ -3652,13 +4133,45 @@ ikev2_set_profile_sa_lifetime (vlib_main_t * vm, u8 * name, return 0; } +static int +ikev2_get_if_address (u32 sw_if_index, ip_address_family_t af, + ip_address_t * out_addr) +{ + ip4_address_t *if_ip4; + ip6_address_t *if_ip6; + + if (af == AF_IP4) + { + if_ip4 = ip4_interface_first_address (&ip4_main, sw_if_index, 0); + if (if_ip4) + { + ip_address_set (out_addr, if_ip4, AF_IP4); + return 1; + } + } + else + { + if_ip6 = ip6_interface_first_address (&ip6_main, sw_if_index); + if (if_ip6) + { + ip_address_set (out_addr, if_ip6, AF_IP6); + return 1; + } + } + return 0; +} + clib_error_t * ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) { ikev2_profile_t *p; clib_error_t *r; - ip4_main_t *im = &ip4_main; ikev2_main_t *km = &ikev2_main; + vlib_buffer_t *b0; + ike_header_t *ike0; + u32 bi0 = 0; + int len = sizeof (ike_header_t), valid_ip = 0; + ip_address_t if_ip = { }; p = ikev2_profile_index_by_name (name); @@ -3668,150 +4181,165 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) return r; } - if (p->responder.sw_if_index == ~0 || p->responder.ip4.data_u32 == 0) + if (p->responder.sw_if_index == ~0 + || ip_address_is_zero (&p->responder.addr)) { r = clib_error_return (0, "responder not set for profile %v", name); return r; } + if (ikev2_get_if_address (p->responder.sw_if_index, + ip_addr_version (&p->responder.addr), &if_ip)) + { + valid_ip = 1; + } - /* Create the Initiator Request */ - { - ike_header_t *ike0; - u32 bi0 = 0; - ip_lookup_main_t *lm = &im->lookup_main; - u32 if_add_index0; - int len = sizeof (ike_header_t); - - /* Get own iface IP */ - if_add_index0 = - lm->if_address_pool_index_by_sw_if_index[p->responder.sw_if_index]; - ip_interface_address_t *if_add = - pool_elt_at_index (lm->if_address_pool, if_add_index0); - ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add); - - bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); - if (!bi0) - { - char *errmsg = "buffer alloc failure"; - ikev2_log_error (errmsg); - return clib_error_return (0, errmsg); - } + /* Prepare the SA and the IKE payload */ + ikev2_sa_t sa; + clib_memset (&sa, 0, sizeof (ikev2_sa_t)); + ikev2_payload_chain_t *chain = 0; + ikev2_payload_new_chain (chain); - /* Prepare the SA and the IKE payload */ - ikev2_sa_t sa; - clib_memset (&sa, 0, sizeof (ikev2_sa_t)); - ikev2_payload_chain_t *chain = 0; - ikev2_payload_new_chain (chain); - - /* Build the IKE proposal payload */ - ikev2_sa_proposal_t *proposals = 0; - ikev2_set_initiator_proposals (vm, &sa, &p->ike_ts, &proposals, 1); - proposals[0].proposal_num = 1; - proposals[0].protocol_id = IKEV2_PROTOCOL_IKE; - - /* Add and then cleanup proposal data */ - ikev2_payload_add_sa (chain, proposals); - ikev2_sa_free_proposal_vector (&proposals); - - sa.is_initiator = 1; - sa.profile_index = p - km->profiles; - sa.state = IKEV2_STATE_SA_INIT; - sa.tun_itf = p->tun_itf; - sa.udp_encap = p->udp_encap; - sa.ipsec_over_udp_port = p->ipsec_over_udp_port; - sa.is_tun_itf_set = 1; - sa.initial_contact = 1; - sa.dst_port = IKEV2_PORT; - ikev2_generate_sa_init_data (&sa); - ikev2_payload_add_ke (chain, sa.dh_group, sa.i_dh_data); - ikev2_payload_add_nonce (chain, sa.i_nonce); - - /* Build the child SA proposal */ - vec_resize (sa.childs, 1); - ikev2_set_initiator_proposals (vm, &sa, &p->esp_ts, - &sa.childs[0].i_proposals, 0); - sa.childs[0].i_proposals[0].proposal_num = 1; - sa.childs[0].i_proposals[0].protocol_id = IKEV2_PROTOCOL_ESP; - RAND_bytes ((u8 *) & sa.childs[0].i_proposals[0].spi, - sizeof (sa.childs[0].i_proposals[0].spi)); - - /* Add NAT detection notification messages (mandatory) */ - u8 *nat_detection_sha1 = - ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi), - clib_host_to_net_u64 (sa.rspi), - clib_host_to_net_u32 (if_ip->as_u32), - clib_host_to_net_u16 (IKEV2_PORT)); - - ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP, - nat_detection_sha1); - vec_free (nat_detection_sha1); - nat_detection_sha1 = - ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi), - clib_host_to_net_u64 (sa.rspi), - clib_host_to_net_u32 (p->responder.ip4.as_u32), - clib_host_to_net_u16 (sa.dst_port)); - ikev2_payload_add_notify (chain, - IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP, - nat_detection_sha1); - vec_free (nat_detection_sha1); - - u8 *sig_hash_algo = vec_new (u8, 8); - u64 tmpsig = clib_host_to_net_u64 (0x0001000200030004); - clib_memcpy_fast (sig_hash_algo, &tmpsig, sizeof (tmpsig)); - ikev2_payload_add_notify (chain, - IKEV2_NOTIFY_MSG_SIGNATURE_HASH_ALGORITHMS, - sig_hash_algo); - vec_free (sig_hash_algo); - - - /* Buffer update and boilerplate */ - len += vec_len (chain->data); - ike0->nextpayload = chain->first_payload_type; - ike0->length = clib_host_to_net_u32 (len); - clib_memcpy_fast (ike0->payload, chain->data, vec_len (chain->data)); - ikev2_payload_destroy_chain (chain); - - ike0->version = IKE_VERSION_2; - ike0->flags = IKEV2_HDR_FLAG_INITIATOR; - ike0->exchange = IKEV2_EXCHANGE_SA_INIT; - ike0->ispi = sa.ispi; - ike0->rspi = 0; - ike0->msgid = 0; - - /* store whole IKE payload - needed for PSK auth */ - vec_free (sa.last_sa_init_req_packet_data); - vec_add (sa.last_sa_init_req_packet_data, ike0, len); - - /* add data to the SA then add it to the pool */ - sa.iaddr.as_u32 = if_ip->as_u32; - sa.raddr.as_u32 = p->responder.ip4.as_u32; - sa.i_id.type = p->loc_id.type; - sa.i_id.data = vec_dup (p->loc_id.data); - sa.r_id.type = p->rem_id.type; - sa.r_id.data = vec_dup (p->rem_id.data); - sa.i_auth.method = p->auth.method; - sa.i_auth.hex = p->auth.hex; - sa.i_auth.data = vec_dup (p->auth.data); - sa.sw_if_index = p->responder.sw_if_index; - vec_add (sa.childs[0].tsi, &p->loc_ts, 1); - vec_add (sa.childs[0].tsr, &p->rem_ts, 1); - - ikev2_initial_contact_cleanup (&sa); - - /* add SA to the pool */ - ikev2_sa_t *sa0 = 0; - pool_get (km->sais, sa0); - clib_memcpy_fast (sa0, &sa, sizeof (*sa0)); - hash_set (km->sa_by_ispi, sa0->ispi, sa0 - km->sais); - - ikev2_send_ike (vm, if_ip, &p->responder.ip4, bi0, len, - IKEV2_PORT, sa.dst_port, sa.sw_if_index); - - ikev2_elog_exchange ("ispi %lx rspi %lx IKEV2_EXCHANGE_SA_INIT sent to " - "%d.%d.%d.%d", clib_host_to_net_u64 (sa0->ispi), 0, - p->responder.ip4.as_u32); - } + /* Build the IKE proposal payload */ + ikev2_sa_proposal_t *proposals = 0; + ikev2_set_initiator_proposals (vm, &sa, &p->ike_ts, &proposals, 1); + proposals[0].proposal_num = 1; + proposals[0].protocol_id = IKEV2_PROTOCOL_IKE; + + /* Add and then cleanup proposal data */ + ikev2_payload_add_sa (chain, proposals); + ikev2_sa_free_proposal_vector (&proposals); + + sa.is_initiator = 1; + sa.profile_index = p - km->profiles; + sa.state = IKEV2_STATE_SA_INIT; + sa.tun_itf = p->tun_itf; + sa.udp_encap = p->udp_encap; + if (p->natt_disabled) + sa.natt_state = IKEV2_NATT_DISABLED; + sa.ipsec_over_udp_port = p->ipsec_over_udp_port; + sa.is_tun_itf_set = 1; + sa.initial_contact = 1; + sa.dst_port = IKEV2_PORT; + + ikev2_generate_sa_error_t rc = ikev2_generate_sa_init_data (&sa); + if (rc != IKEV2_GENERATE_SA_INIT_OK) + { + ikev2_sa_free_all_vec (&sa); + ikev2_payload_destroy_chain (chain); + return clib_error_return (0, "%U", format_ikev2_gen_sa_error, rc); + } + + ikev2_payload_add_ke (chain, sa.dh_group, sa.i_dh_data); + ikev2_payload_add_nonce (chain, sa.i_nonce); + + /* Build the child SA proposal */ + vec_resize (sa.childs, 1); + ikev2_set_initiator_proposals (vm, &sa, &p->esp_ts, + &sa.childs[0].i_proposals, 0); + sa.childs[0].i_proposals[0].proposal_num = 1; + sa.childs[0].i_proposals[0].protocol_id = IKEV2_PROTOCOL_ESP; + RAND_bytes ((u8 *) & sa.childs[0].i_proposals[0].spi, + sizeof (sa.childs[0].i_proposals[0].spi)); + + /* Add NAT detection notification messages (mandatory) */ + u8 *nat_detection_sha1 = + ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi), + clib_host_to_net_u64 (sa.rspi), + &if_ip, clib_host_to_net_u16 (IKEV2_PORT)); + + ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP, + nat_detection_sha1); + vec_free (nat_detection_sha1); + nat_detection_sha1 = + ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi), + clib_host_to_net_u64 (sa.rspi), + &p->responder.addr, + clib_host_to_net_u16 (sa.dst_port)); + ikev2_payload_add_notify (chain, + IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP, + nat_detection_sha1); + vec_free (nat_detection_sha1); + + u8 *sig_hash_algo = vec_new (u8, 8); + u64 tmpsig = clib_host_to_net_u64 (0x0001000200030004); + clib_memcpy_fast (sig_hash_algo, &tmpsig, sizeof (tmpsig)); + ikev2_payload_add_notify (chain, + IKEV2_NOTIFY_MSG_SIGNATURE_HASH_ALGORITHMS, + sig_hash_algo); + vec_free (sig_hash_algo); + + bi0 = ikev2_get_new_ike_header_buff (vm, &b0); + if (!bi0) + { + ikev2_sa_free_all_vec (&sa); + ikev2_payload_destroy_chain (chain); + char *errmsg = "buffer alloc failure"; + ikev2_log_error (errmsg); + return clib_error_return (0, errmsg); + } + ike0 = vlib_buffer_get_current (b0); + + /* Buffer update and boilerplate */ + len += vec_len (chain->data); + ike0->nextpayload = chain->first_payload_type; + ike0->length = clib_host_to_net_u32 (len); + clib_memcpy_fast (ike0->payload, chain->data, vec_len (chain->data)); + ikev2_payload_destroy_chain (chain); + + ike0->version = IKE_VERSION_2; + ike0->flags = IKEV2_HDR_FLAG_INITIATOR; + ike0->exchange = IKEV2_EXCHANGE_SA_INIT; + ike0->ispi = clib_host_to_net_u64 (sa.ispi); + ike0->rspi = 0; + ike0->msgid = 0; + sa.last_init_msg_id += 1; + + /* store whole IKE payload - needed for PSK auth */ + vec_reset_length (sa.last_sa_init_req_packet_data); + vec_add (sa.last_sa_init_req_packet_data, ike0, len); + + /* add data to the SA then add it to the pool */ + ip_address_copy (&sa.iaddr, &if_ip); + ip_address_copy (&sa.raddr, &p->responder.addr); + sa.i_id.type = p->loc_id.type; + sa.i_id.data = vec_dup (p->loc_id.data); + sa.r_id.type = p->rem_id.type; + sa.r_id.data = vec_dup (p->rem_id.data); + sa.i_auth.method = p->auth.method; + sa.i_auth.hex = p->auth.hex; + sa.i_auth.data = vec_dup (p->auth.data); + sa.sw_if_index = p->responder.sw_if_index; + vec_add (sa.childs[0].tsi, &p->loc_ts, 1); + vec_add (sa.childs[0].tsr, &p->rem_ts, 1); + + ikev2_initial_contact_cleanup (0, &sa); + + /* add SA to the pool */ + ikev2_sa_t *sa0 = 0; + pool_get (km->sais, sa0); + clib_memcpy_fast (sa0, &sa, sizeof (*sa0)); + hash_set (km->sa_by_ispi, sa0->ispi, sa0 - km->sais); + + if (valid_ip) + { + ikev2_send_ike (vm, &if_ip, &p->responder.addr, bi0, len, + IKEV2_PORT, sa.dst_port, sa.sw_if_index); + + ikev2_elog_exchange + ("ispi %lx rspi %lx IKEV2_EXCHANGE_SA_INIT sent to ", + clib_host_to_net_u64 (sa0->ispi), 0, + ip_addr_v4 (&p->responder.addr).as_u32, + ip_addr_version (&p->responder.addr) == AF_IP4); + } + else + { + r = + clib_error_return (0, "interface %U does not have any IP address!", + format_vnet_sw_if_index_name, vnet_get_main (), + p->responder.sw_if_index); + return r; + } return 0; } @@ -3824,25 +4352,31 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, ikev2_main_t *km = &ikev2_main; ike_header_t *ike0; u32 bi0 = 0; + vlib_buffer_t *b0; int len; - bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); + bi0 = ikev2_get_new_ike_header_buff (vm, &b0); if (!bi0) { ikev2_log_error ("buffer alloc failure"); return; } + ike0 = vlib_buffer_get_current (b0); ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL; ike0->ispi = clib_host_to_net_u64 (sa->ispi); ike0->rspi = clib_host_to_net_u64 (sa->rspi); + ike0->flags = 0; vec_resize (sa->del, 1); sa->del->protocol_id = IKEV2_PROTOCOL_ESP; sa->del->spi = csa->i_proposals->spi; - ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1); - sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); - len = ikev2_generate_message (sa, ike0, 0, 0); - if (sa->natt) + ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); + sa->last_init_msg_id += 1; + len = ikev2_generate_message (b0, sa, ike0, 0, 0); + if (~0 == len) + return; + + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len, ikev2_get_port (sa), sa->dst_port, sa->sw_if_index); @@ -3925,7 +4459,7 @@ ikev2_initiate_delete_ike_sa (vlib_main_t * vm, u64 ispi) return r; } - ikev2_initiate_delete_ike_sa_internal (vm, ftkm, fsa); + ikev2_initiate_delete_ike_sa_internal (vm, ftkm, fsa, 1); return 0; } @@ -3935,25 +4469,28 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, { /* Create the Initiator request for create child SA */ ike_header_t *ike0; + vlib_buffer_t *b0; u32 bi0 = 0; int len; - bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); + bi0 = ikev2_get_new_ike_header_buff (vm, &b0); if (!bi0) { ikev2_log_error ("buffer alloc failure"); return; } + ike0 = vlib_buffer_get_current (b0); ike0->version = IKE_VERSION_2; ike0->flags = IKEV2_HDR_FLAG_INITIATOR; ike0->exchange = IKEV2_EXCHANGE_CREATE_CHILD_SA; ike0->ispi = clib_host_to_net_u64 (sa->ispi); ike0->rspi = clib_host_to_net_u64 (sa->rspi); - ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1); - sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); + ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); + sa->last_init_msg_id += 1; ikev2_rekey_t *rekey; + vec_reset_length (sa->rekey); vec_add2 (sa->rekey, rekey, 1); ikev2_sa_proposal_t *proposals = vec_dup (csa->i_proposals); @@ -3961,8 +4498,11 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, RAND_bytes ((u8 *) & proposals[0].spi, sizeof (proposals[0].spi)); rekey->spi = proposals[0].spi; rekey->ispi = csa->i_proposals->spi; - len = ikev2_generate_message (sa, ike0, proposals, 0); - if (sa->natt) + len = ikev2_generate_message (b0, sa, ike0, proposals, 0); + if (~0 == len) + return; + + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len, ikev2_get_port (sa), ikev2_get_port (sa), sa->sw_if_index); @@ -4009,6 +4549,74 @@ ikev2_initiate_rekey_child_sa (vlib_main_t * vm, u32 ispi) return 0; } +static int +ikev2_sa_sw_if_match (ikev2_sa_t * sa, u32 sw_if_index) +{ + return (sa->sw_if_index == sw_if_index) && sa->is_initiator; +} + +static void +ikev2_sa_del (ikev2_profile_t * p, u32 sw_if_index) +{ + u64 *ispi, *ispi_vec = 0; + ikev2_sa_t *sa, **sap, **sa_vec = 0; + ikev2_main_t *km = &ikev2_main; + ikev2_main_per_thread_data_t *tkm; + p->responder.sw_if_index = ~0; + + vec_foreach (tkm, km->per_thread_data) + { + /* *INDENT-OFF* */ + pool_foreach (sa, tkm->sas, ({ + if (ikev2_sa_sw_if_match (sa, sw_if_index)) + vec_add1 (sa_vec, sa); + })); + /* *INDENT-ON* */ + + vec_foreach (sap, sa_vec) + { + ikev2_initiate_delete_ike_sa_internal (km->vlib_main, tkm, *sap, 0); + } + vec_reset_length (sa_vec); + } + vec_free (sa_vec); + + /* *INDENT-OFF* */ + pool_foreach (sa, km->sais, ({ + if (ikev2_sa_sw_if_match (sa, sw_if_index)) + vec_add1 (ispi_vec, sa->ispi); + })); + /* *INDENT-ON* */ + + vec_foreach (ispi, ispi_vec) + { + ikev2_del_sa_init_from_main (ispi); + } + + vec_free (ispi_vec); +} + +static clib_error_t * +ikev2_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) +{ + ikev2_main_t *km = &ikev2_main; + ikev2_profile_t *p; + + if (is_add) + return 0; + + /* *INDENT-OFF* */ + pool_foreach (p, km->profiles, ({ + if (p->responder.sw_if_index == sw_if_index) + ikev2_sa_del (p, sw_if_index); + })); + /* *INDENT-ON* */ + + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ikev2_sw_interface_add_del); + clib_error_t * ikev2_init (vlib_main_t * vm) { @@ -4050,12 +4658,14 @@ ikev2_init (vlib_main_t * vm) km->sw_if_indices = hash_create (0, 0); km->udp_ports = hash_create (0, sizeof (uword)); - udp_register_dst_port (vm, IKEV2_PORT, ikev2_node.index, 1); - udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node.index, 1); + udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip4.index, 1); + udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip6.index, 0); + udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip4.index, 1); + udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip6.index, 0); - vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("ikev2"); + vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("ikev2-ip4"); vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], - "ikev2"); + "ikev2-ip4"); ikev2_cli_reference (); km->log_level = IKEV2_LOG_ERROR; @@ -4126,13 +4736,17 @@ ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa, ip46_address_t remote_ip; if (sa->is_initiator) { - ip46_address_set_ip4 (&local_ip, &sa->iaddr); - ip46_address_set_ip4 (&remote_ip, &sa->raddr); + local_ip = to_ip46 (ip_addr_version (&sa->iaddr), + ip_addr_bytes (&sa->iaddr)); + remote_ip = to_ip46 (ip_addr_version (&sa->raddr), + ip_addr_bytes (&sa->raddr)); } else { - ip46_address_set_ip4 (&local_ip, &sa->raddr); - ip46_address_set_ip4 (&remote_ip, &sa->iaddr); + local_ip = to_ip46 (ip_addr_version (&sa->raddr), + ip_addr_bytes (&sa->raddr)); + remote_ip = to_ip46 (ip_addr_version (&sa->iaddr), + ip_addr_bytes (&sa->iaddr)); } /* *INDENT-OFF* */ @@ -4155,7 +4769,10 @@ ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa, u32 *sas_in = NULL; vec_add1 (sas_in, csa->remote_sa_id); vlib_worker_thread_barrier_sync (vm); - ipsec_tun_protect_update (sw_if_index, NULL, csa->local_sa_id, sas_in); + int rv = ipsec_tun_protect_update (sw_if_index, NULL, + csa->local_sa_id, sas_in); + if (rv) + vec_free (sas_in); ipsec_sa_unlock_id (ikev2_flip_alternate_sa_bit (csa->remote_sa_id)); vlib_worker_thread_barrier_release (vm); } @@ -4191,6 +4808,17 @@ ikev2_set_liveness_params (u32 period, u32 max_retries) return 0; } +clib_error_t * +ikev2_profile_natt_disable (u8 * name) +{ + ikev2_profile_t *p = ikev2_profile_index_by_name (name); + if (!p) + return clib_error_return (0, "unknown profile %v", name); + + p->natt_disabled = 1; + return 0; +} + static void ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) { @@ -4235,6 +4863,51 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) } } +static void +ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa) +{ + ikev2_profile_t *p; + u32 bi0; + u8 *nat_sha, *np; + + if (ip_address_is_zero (&sa->iaddr)) + { + p = pool_elt_at_index (km->profiles, sa->profile_index); + if (!ikev2_get_if_address (p->responder.sw_if_index, + ip_addr_version (&p->responder.addr), + &sa->iaddr)) + return; + + /* update NAT detection payload */ + np = + ikev2_find_ike_notify_payload + ((ike_header_t *) sa->last_sa_init_req_packet_data, + IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP); + if (np) + { + nat_sha = + ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa->ispi), + clib_host_to_net_u64 (sa->rspi), + &sa->iaddr, + clib_host_to_net_u16 (IKEV2_PORT)); + clib_memcpy_fast (np, nat_sha, vec_len (nat_sha)); + vec_free (nat_sha); + } + } + + if (vlib_buffer_alloc (km->vlib_main, &bi0, 1) != 1) + return; + + vlib_buffer_t *b = vlib_get_buffer (km->vlib_main, bi0); + clib_memcpy_fast (vlib_buffer_get_current (b), + sa->last_sa_init_req_packet_data, + vec_len (sa->last_sa_init_req_packet_data)); + + ikev2_send_ike (km->vlib_main, &sa->iaddr, &sa->raddr, bi0, + vec_len (sa->last_sa_init_req_packet_data), + ikev2_get_port (sa), IKEV2_PORT, sa->sw_if_index); +} + static void ikev2_process_pending_sa_init (ikev2_main_t * km) { @@ -4249,47 +4922,41 @@ ikev2_process_pending_sa_init (ikev2_main_t * km) if (sa->init_response_received) continue; - u32 bi0; - if (vlib_buffer_alloc (km->vlib_main, &bi0, 1) != 1) - return; - - vlib_buffer_t * b = vlib_get_buffer (km->vlib_main, bi0); - clib_memcpy_fast (vlib_buffer_get_current (b), - sa->last_sa_init_req_packet_data, - vec_len (sa->last_sa_init_req_packet_data)); - ikev2_send_ike (km->vlib_main, &sa->iaddr, &sa->raddr, bi0, - vec_len (sa->last_sa_init_req_packet_data), - ikev2_get_port (sa), IKEV2_PORT, sa->sw_if_index); + ikev2_process_pending_sa_init_one (km, sa); })); /* *INDENT-ON* */ } -static vlib_node_registration_t ikev2_mngr_process_node; - static void ikev2_send_informational_request (ikev2_sa_t * sa) { ikev2_main_t *km = &ikev2_main; - ip4_address_t *src, *dst; + ip_address_t *src, *dst; ike_header_t *ike0; + vlib_buffer_t *b0; u32 bi0 = 0; u16 dp; int len; - bi0 = ikev2_get_new_ike_header_buff (km->vlib_main, &ike0); + bi0 = ikev2_get_new_ike_header_buff (km->vlib_main, &b0); if (!bi0) { ikev2_log_error ("buffer alloc failure"); return; } + ike0 = vlib_buffer_get_current (b0); ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL; ike0->ispi = clib_host_to_net_u64 (sa->ispi); ike0->rspi = clib_host_to_net_u64 (sa->rspi); - ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1); - sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); - len = ikev2_generate_message (sa, ike0, 0, 0); - if (sa->natt) + ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); + ike0->flags = 0; + sa->last_init_msg_id += 1; + len = ikev2_generate_message (b0, sa, ike0, 0, 0); + if (~0 == len) + return; + + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); if (sa->is_initiator) @@ -4308,6 +4975,13 @@ ikev2_send_informational_request (ikev2_sa_t * sa) sa->sw_if_index); } +void +ikev2_disable_dpd (void) +{ + ikev2_main_t *km = &ikev2_main; + km->dpd_disabled = 1; +} + static_always_inline int ikev2_mngr_process_responder_sas (ikev2_sa_t * sa) { @@ -4343,8 +5017,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, while (1) { - u8 req_sent = 0; - vlib_process_wait_for_event_or_clock (vm, 1); + vlib_process_wait_for_event_or_clock (vm, 2); vlib_process_get_events (vm, NULL); /* process ike child sas */ @@ -4358,6 +5031,10 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, pool_foreach (sa, tkm->sas, ({ ikev2_child_sa_t *c; u8 del_old_ids = 0; + + if (sa->state != IKEV2_STATE_AUTHENTICATED) + continue; + if (sa->old_remote_id_present && 0 > sa->old_id_expiration) { sa->old_remote_id_present = 0; @@ -4367,11 +5044,9 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, sa->old_id_expiration -= 1; vec_foreach (c, sa->childs) - { - req_sent |= ikev2_mngr_process_child_sa(sa, c, del_old_ids); - } + ikev2_mngr_process_child_sa(sa, c, del_old_ids); - if (ikev2_mngr_process_responder_sas (sa)) + if (!km->dpd_disabled && ikev2_mngr_process_responder_sas (sa)) vec_add1 (to_be_deleted, sa - tkm->sas); })); /* *INDENT-ON* */ @@ -4394,7 +5069,12 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, p = pool_elt_at_index (km->profiles, sa->profile_index); if (p) { - ikev2_initiate_sa_init (vm, p->name); + clib_error_t *e = ikev2_initiate_sa_init (vm, p->name); + if (e) + { + ikev2_log_error ("%U", format_clib_error, e); + clib_error_free (e); + } } } } @@ -4410,14 +5090,6 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, /* *INDENT-ON* */ ikev2_process_pending_sa_init (km); - - if (req_sent) - { - vlib_process_wait_for_event_or_clock (vm, 5); - vlib_process_get_events (vm, NULL); - req_sent = 0; - } - } return 0; } diff --git a/src/plugins/ikev2/ikev2.h b/src/plugins/ikev2/ikev2.h index d435179275aa..893d9544aa82 100644 --- a/src/plugins/ikev2/ikev2.h +++ b/src/plugins/ikev2/ikev2.h @@ -44,14 +44,20 @@ typedef CLIB_PACKED (struct { }) ike_header_t; /* *INDENT-ON* */ +#define ike_hdr_is_response(_h) ((_h)->flags & IKEV2_HDR_FLAG_RESPONSE) +#define ike_hdr_is_request(_h) (!ike_hdr_is_response(_h)) +#define ike_hdr_is_initiator(_h) ((_h)->flags & IKEV2_HDR_FLAG_INITIATOR) +#define ike_hdr_is_responder(_h) (!(ike_hdr_is_initiator(_h))) + /* *INDENT-OFF* */ -typedef CLIB_PACKED (struct - { - u8 nextpayload; - u8 flags; - u16 length; - u16 dh_group; - u8 reserved[2]; u8 payload[0];}) ike_ke_payload_header_t; +typedef CLIB_PACKED (struct { + u8 nextpayload; + u8 flags; + u16 length; + u16 dh_group; + u8 reserved[2]; + u8 payload[0]; +}) ike_ke_payload_header_t; /* *INDENT-ON* */ /* *INDENT-OFF* */ @@ -368,6 +374,12 @@ typedef enum #undef _ } ikev2_id_type_t; +typedef enum +{ + TS_IPV4_ADDR_RANGE = 7, + TS_IPV6_ADDR_RANGE = 8, +} ikev2_traffic_selector_type_t; + clib_error_t *ikev2_init (vlib_main_t * vm); clib_error_t *ikev2_set_local_key (vlib_main_t * vm, u8 * file); clib_error_t *ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add); @@ -378,11 +390,11 @@ clib_error_t *ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data, int is_local); clib_error_t *ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id, u16 start_port, - u16 end_port, ip4_address_t start_addr, - ip4_address_t end_addr, int is_local); + u16 end_port, ip_address_t start_addr, + ip_address_t end_addr, int is_local); clib_error_t *ikev2_set_profile_responder (vlib_main_t * vm, u8 * name, u32 sw_if_index, - ip4_address_t ip4); + ip_address_t addr); clib_error_t *ikev2_set_profile_ike_transforms (vlib_main_t * vm, u8 * name, ikev2_transform_encr_type_t crypto_alg, diff --git a/src/plugins/ikev2/ikev2_api.c b/src/plugins/ikev2/ikev2_api.c index c73505b2421c..dd4c094c50e4 100644 --- a/src/plugins/ikev2/ikev2_api.c +++ b/src/plugins/ikev2/ikev2_api.c @@ -98,8 +98,8 @@ cp_ts (vl_api_ikev2_ts_t * vl_api_ts, ikev2_ts_t * ts, u8 is_local) vl_api_ts->protocol_id = ts->protocol_id; vl_api_ts->start_port = ts->start_port; vl_api_ts->end_port = ts->end_port; - ip4_address_encode (&ts->start_addr, vl_api_ts->start_addr); - ip4_address_encode (&ts->end_addr, vl_api_ts->end_addr); + ip_address_encode2 (&ts->start_addr, &vl_api_ts->start_addr); + ip_address_encode2 (&ts->end_addr, &vl_api_ts->end_addr); } static void @@ -116,7 +116,7 @@ cp_responder (vl_api_ikev2_responder_t * vl_api_responder, ikev2_responder_t * responder) { vl_api_responder->sw_if_index = responder->sw_if_index; - ip4_address_encode (&responder->ip4, vl_api_responder->ip4); + ip_address_encode2 (&responder->addr, &vl_api_responder->addr); } void @@ -163,7 +163,7 @@ send_profile (ikev2_profile_t * profile, vl_api_registration_t * reg, rmp->profile.udp_encap = profile->udp_encap; rmp->profile.tun_itf = profile->tun_itf; - + rmp->profile.natt_disabled = profile->natt_disabled; rmp->profile.ipsec_over_udp_port = profile->ipsec_over_udp_port; rmp->profile.lifetime = profile->lifetime; @@ -208,8 +208,8 @@ send_sa (ikev2_sa_t * sa, vl_api_ikev2_sa_dump_t * mp, u32 api_sa_index) vl_api_ikev2_keys_t* k = &rsa->keys; rsa->profile_index = rsa->profile_index; rsa->sa_index = api_sa_index; - ip4_address_encode (&sa->iaddr, rsa->iaddr); - ip4_address_encode (&sa->raddr, rsa->raddr); + ip_address_encode2 (&sa->iaddr, &rsa->iaddr); + ip_address_encode2 (&sa->raddr, &rsa->raddr); rsa->ispi = sa->ispi; rsa->rspi = sa->rspi; cp_id(&rsa->i_id, &sa->i_id); @@ -481,7 +481,11 @@ static void error = ikev2_set_liveness_params (clib_net_to_host_u32 (mp->period), clib_net_to_host_u32 (mp->max_retries)); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -502,7 +506,11 @@ vl_api_ikev2_profile_add_del_t_handler (vl_api_ikev2_profile_add_del_t * mp) error = ikev2_add_del_profile (vm, tmp, mp->is_add); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -528,7 +536,11 @@ static void vec_free (tmp); vec_free (data); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -553,7 +565,11 @@ vl_api_ikev2_profile_set_id_t_handler (vl_api_ikev2_profile_set_id_t * mp) vec_free (tmp); vec_free (data); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -575,7 +591,11 @@ static void error = ikev2_set_profile_udp_encap (vm, tmp); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -593,9 +613,9 @@ vl_api_ikev2_profile_set_ts_t_handler (vl_api_ikev2_profile_set_ts_t * mp) vlib_main_t *vm = vlib_get_main (); clib_error_t *error; u8 *tmp = format (0, "%s", mp->name); - ip4_address_t start_addr, end_addr; - ip4_address_decode (mp->ts.start_addr, &start_addr); - ip4_address_decode (mp->ts.end_addr, &end_addr); + ip_address_t start_addr, end_addr; + ip_address_decode2 (&mp->ts.start_addr, &start_addr); + ip_address_decode2 (&mp->ts.end_addr, &end_addr); error = ikev2_set_profile_ts (vm, tmp, mp->ts.protocol_id, clib_net_to_host_u16 (mp->ts.start_port), @@ -603,7 +623,11 @@ vl_api_ikev2_profile_set_ts_t_handler (vl_api_ikev2_profile_set_ts_t * mp) start_addr, end_addr, mp->ts.is_local); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -623,7 +647,11 @@ vl_api_ikev2_set_local_key_t_handler (vl_api_ikev2_set_local_key_t * mp) error = ikev2_set_local_key (vm, mp->key_file); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -642,14 +670,18 @@ vl_api_ikev2_set_responder_t_handler (vl_api_ikev2_set_responder_t * mp) clib_error_t *error; u8 *tmp = format (0, "%s", mp->name); - ip4_address_t ip4; - ip4_address_decode (mp->responder.ip4, &ip4); + ip_address_t ip; + ip_address_decode2 (&mp->responder.addr, &ip); u32 sw_if_index = clib_net_to_host_u32 (mp->responder.sw_if_index); - error = ikev2_set_profile_responder (vm, tmp, sw_if_index, ip4); + error = ikev2_set_profile_responder (vm, tmp, sw_if_index, ip); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -677,7 +709,11 @@ vl_api_ikev2_set_ike_transforms_t_handler (vl_api_ikev2_set_ike_transforms_t * ntohl (mp->tr.crypto_key_size)); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -704,7 +740,11 @@ vl_api_ikev2_set_esp_transforms_t_handler (vl_api_ikev2_set_esp_transforms_t * ntohl (mp->tr.crypto_key_size)); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -733,7 +773,11 @@ vl_api_ikev2_set_sa_lifetime_t_handler (vl_api_ikev2_set_sa_lifetime_t * mp) (mp->lifetime_maxdata)); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -782,7 +826,11 @@ static void ntohl (mp->sw_if_index)); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } vec_free (tmp); #else rv = VNET_API_ERROR_UNIMPLEMENTED; @@ -807,7 +855,11 @@ vl_api_ikev2_initiate_sa_init_t_handler (vl_api_ikev2_initiate_sa_init_t * mp) error = ikev2_initiate_sa_init (vm, tmp); vec_free (tmp); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -828,7 +880,11 @@ vl_api_ikev2_initiate_del_ike_sa_t_handler (vl_api_ikev2_initiate_del_ike_sa_t error = ikev2_initiate_delete_ike_sa (vm, mp->ispi); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -849,7 +905,11 @@ static void error = ikev2_initiate_delete_child_sa (vm, mp->ispi); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif @@ -857,6 +917,32 @@ static void REPLY_MACRO (VL_API_IKEV2_INITIATE_DEL_CHILD_SA_REPLY); } +static void + vl_api_ikev2_profile_disable_natt_t_handler + (vl_api_ikev2_profile_disable_natt_t * mp) +{ + vl_api_ikev2_profile_disable_natt_reply_t *rmp; + int rv = 0; + +#if WITH_LIBSSL > 0 + clib_error_t *error; + + u8 *tmp = format (0, "%s", mp->name); + error = ikev2_profile_natt_disable (tmp); + vec_free (tmp); + if (error) + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } +#else + rv = VNET_API_ERROR_UNIMPLEMENTED; +#endif + + REPLY_MACRO (VL_API_IKEV2_PROFILE_DISABLE_NATT_REPLY); +} + static void vl_api_ikev2_initiate_rekey_child_sa_t_handler (vl_api_ikev2_initiate_rekey_child_sa_t * mp) @@ -870,7 +956,11 @@ static void error = ikev2_initiate_rekey_child_sa (vm, mp->ispi); if (error) - rv = VNET_API_ERROR_UNSPECIFIED; + { + ikev2_log_error ("%U", format_clib_error, error); + clib_error_free (error); + rv = VNET_API_ERROR_UNSPECIFIED; + } #else rv = VNET_API_ERROR_UNIMPLEMENTED; #endif diff --git a/src/plugins/ikev2/ikev2_cli.c b/src/plugins/ikev2/ikev2_cli.c index 687e6f24d87b..3290b59ed68d 100644 --- a/src/plugins/ikev2/ikev2_cli.c +++ b/src/plugins/ikev2/ikev2_cli.c @@ -46,135 +46,243 @@ format_ikev2_id_type_and_data (u8 * s, va_list * args) return s; } +static u8 * +format_ikev2_traffic_selector (u8 * s, va_list * va) +{ + ikev2_ts_t *ts = va_arg (*va, ikev2_ts_t *); + u32 index = va_arg (*va, u32); + + s = format (s, "%u type %u protocol_id %u addr " + "%U - %U port %u - %u\n", + index, ts->ts_type, ts->protocol_id, + format_ip_address, &ts->start_addr, + format_ip_address, &ts->end_addr, + clib_net_to_host_u16 (ts->start_port), + clib_net_to_host_u16 (ts->end_port)); + return s; +} -static clib_error_t * -show_ikev2_sa_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) +static u8 * +format_ikev2_child_sa (u8 * s, va_list * va) { - ikev2_main_t *km = &ikev2_main; - ikev2_main_per_thread_data_t *tkm; - ikev2_sa_t *sa; + ikev2_child_sa_t *child = va_arg (*va, ikev2_child_sa_t *); + u32 index = va_arg (*va, u32); ikev2_ts_t *ts; - ikev2_child_sa_t *child; ikev2_sa_transform_t *tr; + u8 *c = 0; - vec_foreach (tkm, km->per_thread_data) - { - /* *INDENT-OFF* */ - pool_foreach (sa, tkm->sas, ({ - u8 * s = 0; - vlib_cli_output(vm, " iip %U ispi %lx rip %U rspi %lx", - format_ip4_address, &sa->iaddr, sa->ispi, - format_ip4_address, &sa->raddr, sa->rspi); + u32 indent = format_get_indent (s); + indent += 1; - tr = ikev2_sa_get_td_for_type(sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); - s = format(s, "%U ", format_ikev2_sa_transform, tr); + s = format (s, "child sa %u:", index); - tr = ikev2_sa_get_td_for_type(sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); - s = format(s, "%U ", format_ikev2_sa_transform, tr); + tr = ikev2_sa_get_td_for_type (child->r_proposals, + IKEV2_TRANSFORM_TYPE_ENCR); + c = format (c, "%U ", format_ikev2_sa_transform, tr); - tr = ikev2_sa_get_td_for_type(sa->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG); - s = format(s, "%U ", format_ikev2_sa_transform, tr); + tr = ikev2_sa_get_td_for_type (child->r_proposals, + IKEV2_TRANSFORM_TYPE_INTEG); + c = format (c, "%U ", format_ikev2_sa_transform, tr); - tr = ikev2_sa_get_td_for_type(sa->r_proposals, IKEV2_TRANSFORM_TYPE_DH); - s = format(s, "%U ", format_ikev2_sa_transform, tr); + tr = ikev2_sa_get_td_for_type (child->r_proposals, + IKEV2_TRANSFORM_TYPE_ESN); + c = format (c, "%U ", format_ikev2_sa_transform, tr); - vlib_cli_output(vm, " %v", s); - vec_free(s); + s = format (s, "%v\n", c); + vec_free (c); - vlib_cli_output(vm, " nonce i:%U\n r:%U", - format_hex_bytes, sa->i_nonce, vec_len(sa->i_nonce), - format_hex_bytes, sa->r_nonce, vec_len(sa->r_nonce)); + s = format (s, "%Uspi(i) %lx spi(r) %lx\n", format_white_space, indent, + child->i_proposals ? child->i_proposals[0].spi : 0, + child->r_proposals ? child->r_proposals[0].spi : 0); - vlib_cli_output(vm, " SK_d %U", - format_hex_bytes, sa->sk_d, vec_len(sa->sk_d)); - if (sa->sk_ai) - { - vlib_cli_output(vm, " SK_a i:%U\n r:%U", - format_hex_bytes, sa->sk_ai, vec_len(sa->sk_ai), - format_hex_bytes, sa->sk_ar, vec_len(sa->sk_ar)); - } - vlib_cli_output(vm, " SK_e i:%U\n r:%U", - format_hex_bytes, sa->sk_ei, vec_len(sa->sk_ei), - format_hex_bytes, sa->sk_er, vec_len(sa->sk_er)); - vlib_cli_output(vm, " SK_p i:%U\n r:%U", - format_hex_bytes, sa->sk_pi, vec_len(sa->sk_pi), - format_hex_bytes, sa->sk_pr, vec_len(sa->sk_pr)); - - vlib_cli_output(vm, " identifier (i) %U", - format_ikev2_id_type_and_data, &sa->i_id); - vlib_cli_output(vm, " identifier (r) %U", - format_ikev2_id_type_and_data, &sa->r_id); - - vec_foreach(child, sa->childs) - { - vlib_cli_output(vm, " child sa %u:", child - sa->childs); + s = format (s, "%USK_e i:%U\n%Ur:%U\n", + format_white_space, indent, + format_hex_bytes, child->sk_ei, vec_len (child->sk_ei), + format_white_space, indent + 6, + format_hex_bytes, child->sk_er, vec_len (child->sk_er)); + if (child->sk_ai) + { + s = format (s, "%USK_a i:%U\n%Ur:%U\n", + format_white_space, indent, + format_hex_bytes, child->sk_ai, vec_len (child->sk_ai), + format_white_space, indent + 6, + format_hex_bytes, child->sk_ar, vec_len (child->sk_ar)); + } + s = format (s, "%Utraffic selectors (i):", format_white_space, indent); + vec_foreach (ts, child->tsi) + s = format (s, "%U", format_ikev2_traffic_selector, ts, ts - child->tsi); + s = format (s, "%Utraffic selectors (r):", format_white_space, indent); + vec_foreach (ts, child->tsr) + s = format (s, "%U", format_ikev2_traffic_selector, ts, ts - child->tsr); + return s; +} - tr = ikev2_sa_get_td_for_type(child->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); - s = format(s, "%U ", format_ikev2_sa_transform, tr); +static u8 * +format_ikev2_sa (u8 * s, va_list * va) +{ + ikev2_sa_t *sa = va_arg (*va, ikev2_sa_t *); + int details = va_arg (*va, int); + ikev2_sa_transform_t *tr; + ikev2_child_sa_t *child; + u32 indent = 1; - tr = ikev2_sa_get_td_for_type(child->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG); - s = format(s, "%U ", format_ikev2_sa_transform, tr); + s = format (s, "iip %U ispi %lx rip %U rspi %lx", + format_ip_address, &sa->iaddr, sa->ispi, + format_ip_address, &sa->raddr, sa->rspi); + if (!details) + return s; - tr = ikev2_sa_get_td_for_type(child->r_proposals, IKEV2_TRANSFORM_TYPE_ESN); - s = format(s, "%U ", format_ikev2_sa_transform, tr); + s = format (s, "\n%U", format_white_space, indent); - vlib_cli_output(vm, " %v", s); - vec_free(s); + tr = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); + s = format (s, "%U ", format_ikev2_sa_transform, tr); - vlib_cli_output(vm, " spi(i) %lx spi(r) %lx", - child->i_proposals ? child->i_proposals[0].spi : 0, - child->r_proposals ? child->r_proposals[0].spi : 0); + tr = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); + s = format (s, "%U ", format_ikev2_sa_transform, tr); - vlib_cli_output(vm, " SK_e i:%U\n r:%U", - format_hex_bytes, child->sk_ei, vec_len(child->sk_ei), - format_hex_bytes, child->sk_er, vec_len(child->sk_er)); - if (child->sk_ai) - { - vlib_cli_output(vm, " SK_a i:%U\n r:%U", - format_hex_bytes, child->sk_ai, vec_len(child->sk_ai), - format_hex_bytes, child->sk_ar, vec_len(child->sk_ar)); - vlib_cli_output(vm, " traffic selectors (i):"); - } - vec_foreach(ts, child->tsi) - { - vlib_cli_output(vm, " %u type %u protocol_id %u addr " - "%U - %U port %u - %u", - ts - child->tsi, - ts->ts_type, ts->protocol_id, - format_ip4_address, &ts->start_addr, - format_ip4_address, &ts->end_addr, - clib_net_to_host_u16( ts->start_port), - clib_net_to_host_u16( ts->end_port)); - } - vlib_cli_output(vm, " traffic selectors (r):"); - vec_foreach(ts, child->tsr) + tr = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG); + s = format (s, "%U ", format_ikev2_sa_transform, tr); + + tr = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_DH); + s = format (s, "%U", format_ikev2_sa_transform, tr); + + s = format (s, "\n%U", format_white_space, indent); + + s = format (s, "nonce i:%U\n%Ur:%U\n", + format_hex_bytes, sa->i_nonce, vec_len (sa->i_nonce), + format_white_space, indent + 6, + format_hex_bytes, sa->r_nonce, vec_len (sa->r_nonce)); + + s = format (s, "%USK_d %U\n", format_white_space, indent, + format_hex_bytes, sa->sk_d, vec_len (sa->sk_d)); + if (sa->sk_ai) + { + s = format (s, "%USK_a i:%U\n%Ur:%U\n", + format_white_space, indent, + format_hex_bytes, sa->sk_ai, vec_len (sa->sk_ai), + format_white_space, indent + 6, + format_hex_bytes, sa->sk_ar, vec_len (sa->sk_ar)); + } + s = format (s, "%USK_e i:%U\n%Ur:%U\n", + format_white_space, indent, + format_hex_bytes, sa->sk_ei, vec_len (sa->sk_ei), + format_white_space, indent + 6, + format_hex_bytes, sa->sk_er, vec_len (sa->sk_er)); + s = format (s, "%USK_p i:%U\n%Ur:%U\n", + format_white_space, indent, + format_hex_bytes, sa->sk_pi, vec_len (sa->sk_pi), + format_white_space, indent + 6, + format_hex_bytes, sa->sk_pr, vec_len (sa->sk_pr)); + + s = format (s, "%Uidentifier (i) %U\n", + format_white_space, indent, + format_ikev2_id_type_and_data, &sa->i_id); + s = format (s, "%Uidentifier (r) %U\n", + format_white_space, indent, + format_ikev2_id_type_and_data, &sa->r_id); + + vec_foreach (child, sa->childs) + { + s = format (s, "%U%U", format_white_space, indent + 2, + format_ikev2_child_sa, child, child - sa->childs); + } + + return s; +} + +static clib_error_t * +show_ikev2_sa_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ikev2_main_t *km = &ikev2_main; + ikev2_main_per_thread_data_t *tkm; + ikev2_sa_t *sa; + u64 rspi; + u8 *s = 0; + int details = 0, show_one = 0; + + if (unformat_user (input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "rspi %lx", &rspi)) + { + show_one = 1; + } + else if (unformat (line_input, "details")) + details = 1; + else + break; + } + unformat_free (line_input); + } + + vec_foreach (tkm, km->per_thread_data) + { + /* *INDENT-OFF* */ + pool_foreach (sa, tkm->sas, ({ + if (show_one) + { + if (sa->rspi == rspi) { - vlib_cli_output(vm, " %u type %u protocol_id %u addr " - "%U - %U port %u - %u", - ts - child->tsr, - ts->ts_type, ts->protocol_id, - format_ip4_address, &ts->start_addr, - format_ip4_address, &ts->end_addr, - clib_net_to_host_u16( ts->start_port), - clib_net_to_host_u16( ts->end_port)); + s = format (s, "%U\n", format_ikev2_sa, sa, 1); + break; } } - vlib_cli_output(vm, ""); + else + s = format (s, "%U\n", format_ikev2_sa, sa, details); })); /* *INDENT-ON* */ } + + vlib_cli_output (vm, "%v", s); + vec_free (s); return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_ikev2_sa_command, static) = { .path = "show ikev2 sa", - .short_help = "show ikev2 sa", + .short_help = "show ikev2 sa [rspi ] [details]", .function = show_ikev2_sa_command_fn, }; /* *INDENT-ON* */ +static clib_error_t * +ikev2_disable_dpd_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ikev2_disable_dpd (); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ikev2_cli_disable_dpd_command, static) = { + .path = "ikev2 dpd disable", + .short_help = "ikev2 dpd disable", + .function = ikev2_disable_dpd_command_fn, +}; +/* *INDENT-ON* */ + +static uword +unformat_ikev2_token (unformat_input_t * input, va_list * va) +{ + u8 **string_return = va_arg (*va, u8 **); + const char *token_chars = "a-zA-Z0-9_"; + if (*string_return) + { + /* if string_return was already allocated (eg. because of a previous + * partial match with a successful unformat_token()), we must free it + * before reusing the pointer, otherwise we'll be leaking memory + */ + vec_free (*string_return); + *string_return = 0; + } + return unformat_user (input, unformat_token, token_chars, string_return); +} + static clib_error_t * ikev2_profile_add_del_command_fn (vlib_main_t * vm, unformat_input_t * input, @@ -188,36 +296,30 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, u8 *data = 0; u32 tmp1, tmp2, tmp3; u64 tmp4, tmp5; - ip4_address_t ip4; - ip4_address_t end_addr; + ip_address_t ip, end_addr; u32 responder_sw_if_index = (u32) ~ 0; u32 tun_sw_if_index = (u32) ~ 0; - ip4_address_t responder_ip4; ikev2_transform_encr_type_t crypto_alg; ikev2_transform_integ_type_t integ_alg; ikev2_transform_dh_type_t dh_type; - const char *valid_chars = "a-zA-Z0-9_"; - 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, "add %U", unformat_token, valid_chars, &name)) + if (unformat (line_input, "add %U", unformat_ikev2_token, &name)) { r = ikev2_add_del_profile (vm, name, 1); goto done; } - else - if (unformat - (line_input, "del %U", unformat_token, valid_chars, &name)) + else if (unformat (line_input, "del %U", unformat_ikev2_token, &name)) { r = ikev2_add_del_profile (vm, name, 0); goto done; } else if (unformat (line_input, "set %U auth shared-key-mic string %v", - unformat_token, valid_chars, &name, &data)) + unformat_ikev2_token, &name, &data)) { r = ikev2_set_profile_auth (vm, name, @@ -226,7 +328,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U auth shared-key-mic hex %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_hex_string, &data)) { r = @@ -236,7 +338,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U auth rsa-sig cert-file %v", - unformat_token, valid_chars, &name, &data)) + unformat_ikev2_token, &name, &data)) { r = ikev2_set_profile_auth (vm, name, IKEV2_AUTH_METHOD_RSA_SIG, data, @@ -244,18 +346,18 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U id local %U %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_id_type, &id_type, - unformat_ip4_address, &ip4)) + unformat_ip_address, &ip)) { - data = vec_new (u8, 4); - clib_memcpy (data, ip4.as_u8, 4); + data = vec_new (u8, ip_address_size (&ip)); + clib_memcpy (data, ip_addr_bytes (&ip), ip_address_size (&ip)); r = ikev2_set_profile_id (vm, name, (u8) id_type, data, /*local */ 1); goto done; } else if (unformat (line_input, "set %U id local %U 0x%U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_id_type, &id_type, unformat_hex_string, &data)) { @@ -264,7 +366,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U id local %U %v", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_id_type, &id_type, &data)) { r = @@ -272,18 +374,18 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U id remote %U %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_id_type, &id_type, - unformat_ip4_address, &ip4)) + unformat_ip_address, &ip)) { - data = vec_new (u8, 4); - clib_memcpy (data, ip4.as_u8, 4); + data = vec_new (u8, ip_address_size (&ip)); + clib_memcpy (data, ip_addr_bytes (&ip), ip_address_size (&ip)); r = ikev2_set_profile_id (vm, name, (u8) id_type, data, /*remote */ 0); goto done; } else if (unformat (line_input, "set %U id remote %U 0x%U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_id_type, &id_type, unformat_hex_string, &data)) { @@ -292,7 +394,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U id remote %U %v", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_id_type, &id_type, &data)) { r = ikev2_set_profile_id (vm, name, (u8) id_type, data, /*remote */ @@ -301,41 +403,37 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, } else if (unformat (line_input, "set %U traffic-selector local " "ip-range %U - %U port-range %u - %u protocol %u", - unformat_token, valid_chars, &name, - unformat_ip4_address, &ip4, - unformat_ip4_address, &end_addr, - &tmp1, &tmp2, &tmp3)) + unformat_ikev2_token, &name, + unformat_ip_address, &ip, + unformat_ip_address, &end_addr, &tmp1, &tmp2, &tmp3)) { r = ikev2_set_profile_ts (vm, name, (u8) tmp3, (u16) tmp1, (u16) tmp2, - ip4, end_addr, /*local */ 1); + ip, end_addr, /*local */ 1); goto done; } else if (unformat (line_input, "set %U traffic-selector remote " "ip-range %U - %U port-range %u - %u protocol %u", - unformat_token, valid_chars, &name, - unformat_ip4_address, &ip4, - unformat_ip4_address, &end_addr, - &tmp1, &tmp2, &tmp3)) + unformat_ikev2_token, &name, + unformat_ip_address, &ip, + unformat_ip_address, &end_addr, &tmp1, &tmp2, &tmp3)) { r = ikev2_set_profile_ts (vm, name, (u8) tmp3, (u16) tmp1, (u16) tmp2, - ip4, end_addr, /*remote */ 0); + ip, end_addr, /*remote */ 0); goto done; } else if (unformat (line_input, "set %U responder %U %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_vnet_sw_interface, vnm, - &responder_sw_if_index, unformat_ip4_address, - &responder_ip4)) + &responder_sw_if_index, unformat_ip_address, &ip)) { r = - ikev2_set_profile_responder (vm, name, responder_sw_if_index, - responder_ip4); + ikev2_set_profile_responder (vm, name, responder_sw_if_index, ip); goto done; } else if (unformat (line_input, "set %U tunnel %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_vnet_sw_interface, vnm, &tun_sw_if_index)) { r = ikev2_set_profile_tunnel_interface (vm, name, tun_sw_if_index); @@ -345,7 +443,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, if (unformat (line_input, "set %U ike-crypto-alg %U %u ike-integ-alg %U ike-dh %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_transform_encr_type, &crypto_alg, &tmp1, unformat_ikev2_transform_integ_type, &integ_alg, unformat_ikev2_transform_dh_type, &dh_type)) @@ -359,7 +457,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, if (unformat (line_input, "set %U ike-crypto-alg %U %u ike-dh %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_transform_encr_type, &crypto_alg, &tmp1, unformat_ikev2_transform_dh_type, &dh_type)) { @@ -373,7 +471,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, if (unformat (line_input, "set %U esp-crypto-alg %U %u esp-integ-alg %U", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_transform_encr_type, &crypto_alg, &tmp1, unformat_ikev2_transform_integ_type, &integ_alg)) { @@ -385,7 +483,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, else if (unformat (line_input, "set %U esp-crypto-alg %U %u", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, unformat_ikev2_transform_encr_type, &crypto_alg, &tmp1)) { r = @@ -393,7 +491,7 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U sa-lifetime %lu %u %u %lu", - unformat_token, valid_chars, &name, + unformat_ikev2_token, &name, &tmp4, &tmp1, &tmp2, &tmp5)) { r = @@ -401,19 +499,25 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, goto done; } else if (unformat (line_input, "set %U udp-encap", - unformat_token, valid_chars, &name)) + unformat_ikev2_token, &name)) { r = ikev2_set_profile_udp_encap (vm, name); goto done; } else if (unformat (line_input, "set %U ipsec-over-udp port %u", - unformat_token, valid_chars, &name, &tmp1)) + unformat_ikev2_token, &name, &tmp1)) { int rv = ikev2_set_profile_ipsec_udp_port (vm, name, tmp1, 1); if (rv) r = clib_error_return (0, "Error: %U", format_vnet_api_errno, rv); goto done; } + else if (unformat (line_input, "set %U disable natt", + unformat_ikev2_token, &name)) + { + r = ikev2_profile_natt_disable (name); + goto done; + } else break; } @@ -445,7 +549,8 @@ VLIB_CLI_COMMAND (ikev2_profile_add_del_command, static) = { "ikev2 profile set ike-crypto-alg ike-integ-alg ike-dh \n" "ikev2 profile set esp-crypto-alg " "[esp-integ-alg ]\n" - "ikev2 profile set sa-lifetime ", + "ikev2 profile set sa-lifetime " + "ikev2 profile set disable natt\n", .function = ikev2_profile_add_del_command_fn, }; /* *INDENT-ON* */ @@ -478,7 +583,7 @@ show_ikev2_profile_command_fn (vlib_main_t * vm, if (p->loc_id.type == IKEV2_ID_TYPE_ID_IPV4_ADDR) vlib_cli_output(vm, " local id-type %U data %U", format_ikev2_id_type, p->loc_id.type, - format_ip4_address, p->loc_id.data); + format_ip_address, p->loc_id.data); else if (p->loc_id.type == IKEV2_ID_TYPE_ID_KEY_ID) vlib_cli_output(vm, " local id-type %U data 0x%U", format_ikev2_id_type, p->loc_id.type, @@ -494,7 +599,7 @@ show_ikev2_profile_command_fn (vlib_main_t * vm, if (p->rem_id.type == IKEV2_ID_TYPE_ID_IPV4_ADDR) vlib_cli_output(vm, " remote id-type %U data %U", format_ikev2_id_type, p->rem_id.type, - format_ip4_address, p->rem_id.data); + format_ip_address, p->rem_id.data); else if (p->rem_id.type == IKEV2_ID_TYPE_ID_KEY_ID) vlib_cli_output(vm, " remote id-type %U data 0x%U", format_ikev2_id_type, p->rem_id.type, @@ -505,19 +610,19 @@ show_ikev2_profile_command_fn (vlib_main_t * vm, format_ikev2_id_type, p->rem_id.type, p->rem_id.data); } - if (p->loc_ts.end_addr.as_u32) + if (!ip_address_is_zero (&p->loc_ts.start_addr)) vlib_cli_output(vm, " local traffic-selector addr %U - %U port %u - %u" " protocol %u", - format_ip4_address, &p->loc_ts.start_addr, - format_ip4_address, &p->loc_ts.end_addr, + format_ip_address, &p->loc_ts.start_addr, + format_ip_address, &p->loc_ts.end_addr, p->loc_ts.start_port, p->loc_ts.end_port, p->loc_ts.protocol_id); - if (p->rem_ts.end_addr.as_u32) + if (!ip_address_is_zero (&p->rem_ts.start_addr)) vlib_cli_output(vm, " remote traffic-selector addr %U - %U port %u - %u" " protocol %u", - format_ip4_address, &p->rem_ts.start_addr, - format_ip4_address, &p->rem_ts.end_addr, + format_ip_address, &p->rem_ts.start_addr, + format_ip_address, &p->rem_ts.end_addr, p->rem_ts.start_port, p->rem_ts.end_port, p->rem_ts.protocol_id); if (~0 != p->tun_itf) @@ -526,10 +631,13 @@ show_ikev2_profile_command_fn (vlib_main_t * vm, if (~0 != p->responder.sw_if_index) vlib_cli_output(vm, " responder %U %U", format_vnet_sw_if_index_name, vnet_get_main(), p->responder.sw_if_index, - format_ip4_address, &p->responder.ip4); + format_ip_address, &p->responder.addr); if (p->udp_encap) vlib_cli_output(vm, " udp-encap"); + if (p->natt_disabled) + vlib_cli_output(vm, " NAT-T disabled"); + if (p->ipsec_over_udp_port != IPSEC_UDP_PORT_NONE) vlib_cli_output(vm, " ipsec-over-udp port %d", p->ipsec_over_udp_port); @@ -651,15 +759,12 @@ ikev2_initiate_command_fn (vlib_main_t * vm, u32 tmp1; u64 tmp2; - const char *valid_chars = "a-zA-Z0-9_"; - 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, "sa-init %U", unformat_token, valid_chars, &name)) + if (unformat (line_input, "sa-init %U", unformat_ikev2_token, &name)) { r = ikev2_initiate_sa_init (vm, name); goto done; @@ -699,7 +804,7 @@ VLIB_CLI_COMMAND (ikev2_initiate_command, static) = { "ikev2 initiate sa-init \n" "ikev2 initiate del-child-sa \n" "ikev2 initiate del-sa \n" - "ikev2 initiate rekey-child-sa \n", + "ikev2 initiate rekey-child-sa \n", .function = ikev2_initiate_command_fn, }; /* *INDENT-ON* */ diff --git a/src/plugins/ikev2/ikev2_crypto.c b/src/plugins/ikev2/ikev2_crypto.c index b1fdf890e08d..f5080ed819f6 100644 --- a/src/plugins/ikev2/ikev2_crypto.c +++ b/src/plugins/ikev2/ikev2_crypto.c @@ -349,10 +349,11 @@ ikev2_init_gcm_nonce (u8 * nonce, u8 * salt, u8 * iv) clib_memcpy (nonce + IKEV2_GCM_SALT_SIZE, iv, IKEV2_GCM_IV_SIZE); } -u8 * +int ikev2_decrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, ikev2_sa_transform_t * tr_encr, u8 * data, - int data_len, u8 * aad, u32 aad_len, u8 * tag) + int data_len, u8 * aad, u32 aad_len, u8 * tag, + u32 * out_len) { EVP_CIPHER_CTX *ctx = ptd->evp_ctx; int len = 0; @@ -369,34 +370,33 @@ ikev2_decrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, data += IKEV2_GCM_IV_SIZE; data_len -= IKEV2_GCM_IV_SIZE; - v8 *r = vec_new (u8, data_len); EVP_DecryptInit_ex (ctx, tr_encr->cipher, 0, 0, 0); EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, 12, 0); EVP_DecryptInit_ex (ctx, 0, 0, key, nonce); EVP_DecryptUpdate (ctx, 0, &len, aad, aad_len); - EVP_DecryptUpdate (ctx, r, &len, data, data_len); + EVP_DecryptUpdate (ctx, data, &len, data, data_len); EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_TAG, IKEV2_GCM_ICV_SIZE, tag); - if (EVP_DecryptFinal_ex (ctx, r + len, &len) > 0) + if (EVP_DecryptFinal_ex (ctx, data + len, &len) > 0) { - /* remove padding */ - _vec_len (r) -= r[vec_len (r) - 1] + 1; - return r; + *out_len = data_len - data[data_len - 1] - 1; + return 1; } - vec_free (r); return 0; } -v8 * +int ikev2_decrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, - ikev2_sa_transform_t * tr_encr, u8 * data, int len) + ikev2_sa_transform_t * tr_encr, u8 * data, int len, + u32 * out_len) { EVP_CIPHER_CTX *ctx = ptd->evp_ctx; - int out_len = 0, block_size; + int tmp_len = 0, block_size; u8 *key = sa->is_initiator ? sa->sk_er : sa->sk_ei; block_size = tr_encr->block_size; + u8 *iv = data; /* check if data is multiplier of cipher block size */ if (len % block_size) @@ -404,15 +404,20 @@ ikev2_decrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, ikev2_elog_error ("wrong data length"); return 0; } + data += block_size; + len -= block_size; - v8 *r = vec_new (u8, len - block_size); - EVP_DecryptInit_ex (ctx, tr_encr->cipher, NULL, key, data); - EVP_DecryptUpdate (ctx, r, &out_len, data + block_size, len - block_size); - EVP_DecryptFinal_ex (ctx, r + out_len, &out_len); - /* remove padding */ - _vec_len (r) -= r[vec_len (r) - 1] + 1; + EVP_DecryptInit_ex (ctx, tr_encr->cipher, NULL, key, iv); + EVP_CIPHER_CTX_set_padding (ctx, 0); + EVP_DecryptUpdate (ctx, data, &tmp_len, data, len); - return r; + if (EVP_DecryptFinal_ex (ctx, data + tmp_len, &tmp_len) > 0) + { + *out_len = len - data[len - 1] - 1; + return 1; + } + + return 0; } int @@ -424,6 +429,8 @@ ikev2_encrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, int out_len = 0, len = 0; u8 nonce[IKEV2_GCM_NONCE_SIZE]; u8 *key = sa->is_initiator ? sa->sk_ei : sa->sk_er; + if (!key) + return 0; /* generate IV; its length must be 8 octets for aes-gcm (rfc5282) */ RAND_bytes (dst, IKEV2_GCM_IV_SIZE); @@ -452,6 +459,8 @@ ikev2_encrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, int out_len = 0, len = 0; int bs = tr_encr->block_size; u8 *key = sa->is_initiator ? sa->sk_ei : sa->sk_er; + if (!key) + return 0; /* generate IV */ u8 *iv = dst; @@ -828,6 +837,7 @@ ikev2_load_cert_file (u8 * file) } pkey = X509_get_pubkey (x509); + X509_free (x509); if (pkey == NULL) ikev2_log_error ("get pubkey %s failed", file); diff --git a/src/plugins/ikev2/ikev2_payload.c b/src/plugins/ikev2/ikev2_payload.c index 56bb652e9405..294864d8c439 100644 --- a/src/plugins/ikev2/ikev2_payload.c +++ b/src/plugins/ikev2/ikev2_payload.c @@ -37,14 +37,23 @@ typedef CLIB_PACKED (struct { /* *INDENT-ON* */ /* *INDENT-OFF* */ +typedef CLIB_PACKED (struct { + ip4_address_t start_addr; + ip4_address_t end_addr; +}) ikev2_ip4_addr_pair_t; + +typedef CLIB_PACKED (struct { + ip6_address_t start_addr; + ip6_address_t end_addr; +}) ikev2_ip6_addr_pair_t; + typedef CLIB_PACKED (struct { u8 ts_type; u8 protocol_id; u16 selector_len; u16 start_port; u16 end_port; - ip4_address_t start_addr; - ip4_address_t end_addr; + u8 addr_pair[0]; }) ikev2_ts_payload_entry_t; /* *INDENT-OFF* */ @@ -286,12 +295,46 @@ ikev2_payload_add_auth (ikev2_payload_chain_t * c, ikev2_auth_t * auth) ikev2_payload_add_data (c, auth->data); } +static void +ikev2_payload_add_ts_entry (u8 ** data, ikev2_ts_t * ts) +{ + u8 * tmp; + ikev2_ts_payload_entry_t *entry; + int len = sizeof (*entry); + + if (ts->ts_type == TS_IPV4_ADDR_RANGE) + len += sizeof (ikev2_ip4_addr_pair_t); + else + len += sizeof (ikev2_ip6_addr_pair_t); + + vec_add2 (data[0], tmp, len); + entry = (ikev2_ts_payload_entry_t *) tmp; + entry->ts_type = ts->ts_type; + entry->protocol_id = ts->protocol_id; + entry->selector_len = clib_host_to_net_u16 (len); + entry->start_port = clib_host_to_net_u16 (ts->start_port); + entry->end_port = clib_host_to_net_u16 (ts->end_port); + + if (ts->ts_type == TS_IPV4_ADDR_RANGE) + { + ikev2_ip4_addr_pair_t *pair = (ikev2_ip4_addr_pair_t*) entry->addr_pair; + ip_address_copy_addr (&pair->start_addr, &ts->start_addr); + ip_address_copy_addr (&pair->end_addr, &ts->end_addr); + } + else + { + ikev2_ip6_addr_pair_t *pair = (ikev2_ip6_addr_pair_t*) entry->addr_pair; + ip_address_copy_addr (&pair->start_addr, &ts->start_addr); + ip_address_copy_addr (&pair->end_addr, &ts->end_addr); + } +} + void ikev2_payload_add_ts (ikev2_payload_chain_t * c, ikev2_ts_t * ts, u8 type) { ike_ts_payload_header_t *tsh; ikev2_ts_t *ts2; - u8 *data = 0, *tmp; + u8 *data = 0; tsh = (ike_ts_payload_header_t *) ikev2_payload_add_hdr (c, type, @@ -300,17 +343,9 @@ ikev2_payload_add_ts (ikev2_payload_chain_t * c, ikev2_ts_t * ts, u8 type) vec_foreach (ts2, ts) { - ASSERT (ts2->ts_type == 7); /*TS_IPV4_ADDR_RANGE */ - ikev2_ts_payload_entry_t *entry; - vec_add2 (data, tmp, sizeof (*entry)); - entry = (ikev2_ts_payload_entry_t *) tmp; - entry->ts_type = ts2->ts_type; - entry->protocol_id = ts2->protocol_id; - entry->selector_len = clib_host_to_net_u16 (16); - entry->start_port = clib_host_to_net_u16 (ts2->start_port); - entry->end_port = clib_host_to_net_u16 (ts2->end_port); - entry->start_addr.as_u32 = ts2->start_addr.as_u32; - entry->end_addr.as_u32 = ts2->end_addr.as_u32; + ASSERT (ts2->ts_type == TS_IPV4_ADDR_RANGE || + ts2->ts_type == TS_IPV6_ADDR_RANGE); + ikev2_payload_add_ts_entry (&data, ts2); } ikev2_payload_add_data (c, data); @@ -327,22 +362,27 @@ ikev2_payload_chain_add_padding (ikev2_payload_chain_t * c, int bs) } ikev2_sa_proposal_t * -ikev2_parse_sa_payload (ike_payload_header_t * ikep) +ikev2_parse_sa_payload (ike_payload_header_t * ikep, u32 rlen) { ikev2_sa_proposal_t *v = 0; ikev2_sa_proposal_t *proposal; ikev2_sa_transform_t *transform; u32 plen = clib_net_to_host_u16 (ikep->length); - ike_sa_proposal_data_t *sap; int proposal_ptr = 0; + if (sizeof (*ikep) > rlen) + return 0; + + rlen -= sizeof (*ikep); do { + if (proposal_ptr + sizeof (*sap) > rlen) + goto data_corrupted; + sap = (ike_sa_proposal_data_t *) & ikep->payload[proposal_ptr]; - int i; - int transform_ptr; + int i, transform_ptr; /* IKE proposal should not have SPI */ if (sap->protocol_id == IKEV2_PROTOCOL_IKE && sap->spi_size != 0) @@ -353,6 +393,8 @@ ikev2_parse_sa_payload (ike_payload_header_t * ikep) goto data_corrupted; transform_ptr = proposal_ptr + sizeof (*sap) + sap->spi_size; + if (transform_ptr > rlen) + goto data_corrupted; vec_add2 (v, proposal, 1); proposal->proposal_num = sap->proposal_num; @@ -366,7 +408,9 @@ ikev2_parse_sa_payload (ike_payload_header_t * ikep) for (i = 0; i < sap->num_transforms; i++) { ike_sa_transform_data_t *tr = - (ike_sa_transform_data_t *) & ikep->payload[transform_ptr]; + (ike_sa_transform_data_t *) & ikep->payload[transform_ptr]; + if (transform_ptr + sizeof (*tr) > rlen) + goto data_corrupted; u16 tlen = clib_net_to_host_u16 (tr->transform_len); if (tlen < sizeof (*tr)) @@ -376,9 +420,11 @@ ikev2_parse_sa_payload (ike_payload_header_t * ikep) transform->type = tr->transform_type; transform->transform_id = clib_net_to_host_u16 (tr->transform_id); + if (transform_ptr + tlen > rlen) + goto data_corrupted; if (tlen > sizeof (*tr)) vec_add (transform->attrs, tr->attributes, tlen - sizeof (*tr)); - transform_ptr += tlen; + transform_ptr += tlen; } proposal_ptr += clib_net_to_host_u16 (sap->proposal_len); @@ -398,46 +444,83 @@ ikev2_parse_sa_payload (ike_payload_header_t * ikep) } ikev2_ts_t * -ikev2_parse_ts_payload (ike_payload_header_t * ikep) +ikev2_parse_ts_payload (ike_payload_header_t * ikep, u32 rlen) { ike_ts_payload_header_t *tsp = (ike_ts_payload_header_t *) ikep; ikev2_ts_t *r = 0, *ts; - u8 i; + ikev2_ip4_addr_pair_t *pair4; + ikev2_ip6_addr_pair_t *pair6; + int p = 0, n_left; + ikev2_ts_payload_entry_t *pe; - for (i = 0; i < tsp->num_ts; i++) + if (sizeof (*tsp) > rlen) + return 0; + + rlen -= sizeof (*tsp); + n_left = tsp->num_ts; + + while (n_left && p + sizeof (*pe) < rlen) { - if (tsp->ts[i].ts_type != 7) /* TS_IPV4_ADDR_RANGE */ + pe = (ikev2_ts_payload_entry_t *) (((u8 *)tsp->ts) + p); + p += sizeof (*pe); + + if (pe->ts_type != TS_IPV4_ADDR_RANGE && + pe->ts_type != TS_IPV6_ADDR_RANGE) { ikev2_elog_uint (IKEV2_LOG_ERROR, - "unsupported TS type received (%u)", tsp->ts[i].ts_type); - continue; + "unsupported TS type received (%u)", pe->ts_type); + return 0; } vec_add2 (r, ts, 1); - ts->ts_type = tsp->ts[i].ts_type; - ts->protocol_id = tsp->ts[i].protocol_id; - ts->start_port = tsp->ts[i].start_port; - ts->end_port = tsp->ts[i].end_port; - ts->start_addr.as_u32 = tsp->ts[i].start_addr.as_u32; - ts->end_addr.as_u32 = tsp->ts[i].end_addr.as_u32; + ts->ts_type = pe->ts_type; + ts->protocol_id = pe->protocol_id; + ts->start_port = pe->start_port; + ts->end_port = pe->end_port; + + if (pe->ts_type == TS_IPV4_ADDR_RANGE) + { + pair4 = (ikev2_ip4_addr_pair_t*) pe->addr_pair; + ip_address_set (&ts->start_addr, &pair4->start_addr, AF_IP4); + ip_address_set (&ts->end_addr, &pair4->end_addr, AF_IP4); + p += sizeof (*pair4); + } + else + { + pair6 = (ikev2_ip6_addr_pair_t*) pe->addr_pair; + ip_address_set (&ts->start_addr, &pair6->start_addr, AF_IP6); + ip_address_set (&ts->end_addr, &pair6->end_addr, AF_IP6); + p += sizeof (*pair6); + } + n_left--; } + + if (n_left) + return 0; + return r; } ikev2_notify_t * -ikev2_parse_notify_payload (ike_payload_header_t * ikep) +ikev2_parse_notify_payload (ike_payload_header_t * ikep, u32 rlen) { ike_notify_payload_header_t *n = (ike_notify_payload_header_t *) ikep; - u32 plen = clib_net_to_host_u16 (ikep->length); + u32 plen = clib_net_to_host_u16 (n->length); ikev2_notify_t *r = 0; u32 spi; + if (sizeof (*n) > rlen) + return 0; + r = vec_new (ikev2_notify_t, 1); r->msg_type = clib_net_to_host_u16 (n->msg_type); r->protocol_id = n->protocol_id; if (n->spi_size == 4) { + if (sizeof (spi) + sizeof (*n) > rlen) + goto cleanup; + clib_memcpy (&spi, n->payload, n->spi_size); r->spi = clib_net_to_host_u32 (spi); } @@ -448,15 +531,22 @@ ikev2_parse_notify_payload (ike_payload_header_t * ikep) else { clib_warning ("invalid SPI Size %d", n->spi_size); + goto cleanup; } if (plen > (sizeof (*n) + n->spi_size)) { - vec_add (r->data, n->payload + n->spi_size, - plen - sizeof (*n) - n->spi_size); - } + if (plen <= sizeof (*n) + n->spi_size) + goto cleanup; + u32 data_len = plen - sizeof (*n) - n->spi_size; + vec_add (r->data, n->payload + n->spi_size, data_len); + } return r; + +cleanup: + vec_free (r); + return 0; } void @@ -467,13 +557,16 @@ ikev2_parse_vendor_payload (ike_payload_header_t * ikep) } ikev2_delete_t * -ikev2_parse_delete_payload (ike_payload_header_t * ikep) +ikev2_parse_delete_payload (ike_payload_header_t * ikep, u32 rlen) { - ike_delete_payload_header_t *d = (ike_delete_payload_header_t *) ikep; + ike_delete_payload_header_t * d = (ike_delete_payload_header_t *) ikep; ikev2_delete_t *r = 0, *del; - u16 num_of_spi = clib_net_to_host_u16 (d->num_of_spi); - u16 i = 0; + u16 i, num_of_spi; + + if (rlen < sizeof (*d)) + return 0; + num_of_spi = clib_net_to_host_u16 (d->num_of_spi); if (d->protocol_id == IKEV2_PROTOCOL_IKE) { r = vec_new (ikev2_delete_t, 1); @@ -481,17 +574,44 @@ ikev2_parse_delete_payload (ike_payload_header_t * ikep) } else { - r = vec_new (ikev2_delete_t, num_of_spi); - vec_foreach (del, r) + if (sizeof (*d) + num_of_spi * sizeof (u32) > rlen) + return 0; + + for (i = 0; i < num_of_spi; i++) { - del->protocol_id = d->protocol_id; - del->spi = clib_net_to_host_u32 (d->spi[i++]); + vec_add2 (r, del, 1); + del->protocol_id = d->protocol_id; + del->spi = clib_net_to_host_u32 (d->spi[i]); } } return r; } +u8 * +ikev2_find_ike_notify_payload (ike_header_t * ike, u32 msg_type) +{ + int p = 0; + ike_notify_payload_header_t *n; + ike_payload_header_t *ikep; + u32 payload = ike->nextpayload; + + while (payload != IKEV2_PAYLOAD_NONE) + { + ikep = (ike_payload_header_t *) & ike->payload[p]; + if (payload == IKEV2_PAYLOAD_NOTIFY) + { + n = (ike_notify_payload_header_t *)ikep; + if (n->msg_type == clib_net_to_host_u16 (msg_type)) + return n->payload; + } + u16 plen = clib_net_to_host_u16 (ikep->length); + payload = ikep->nextpayload; + p += plen; + } + return 0; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h index f8b0458db9c8..fa302dcf21a6 100644 --- a/src/plugins/ikev2/ikev2_priv.h +++ b/src/plugins/ikev2/ikev2_priv.h @@ -81,7 +81,7 @@ do { \ } \ } while (0) \ -#define ikev2_elog_exchange(_format, _ispi, _rspi, _addr) \ +#define ikev2_elog_exchange_internal(_format, _ispi, _rspi, _addr) \ do { \ ikev2_main_t *km = &ikev2_main; \ if (PREDICT_FALSE (km->log_level >= IKEV2_LOG_DEBUG)) \ @@ -110,6 +110,17 @@ do { \ } \ } while (0) \ +#define IKE_ELOG_IP4_FMT "%d.%d.%d.%d" +#define IKE_ELOG_IP6_FMT "[v6]:%x%x:%x%x" + +#define ikev2_elog_exchange(_fmt, _ispi, _rspi, _addr, _v4) \ +do { \ + if (_v4) \ + ikev2_elog_exchange_internal (_fmt IKE_ELOG_IP4_FMT, _ispi, _rspi, _addr);\ + else \ + ikev2_elog_exchange_internal (_fmt IKE_ELOG_IP6_FMT, _ispi, _rspi, _addr);\ +} while (0) + #define ikev2_elog_uint(_level, _format, _val) \ do { \ ikev2_main_t *km = &ikev2_main; \ @@ -156,31 +167,6 @@ do { \ } \ } while (0) -#define ikev2_elog_peers(_level, _format, _ip1, _ip2) \ -do { \ - ikev2_main_t *km = &ikev2_main; \ - if (PREDICT_FALSE (km->log_level >= _level)) \ - { \ - ELOG_TYPE_DECLARE (e) = \ - { \ - .format = "ikev2: " _format, \ - .format_args = "i1i1i1i1i1i1i1i1", \ - }; \ - CLIB_PACKED(struct { \ - u8 i11; u8 i12; u8 i13; u8 i14; \ - u8 i21; u8 i22; u8 i23; u8 i24; }) *ed; \ - ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ - ed->i14 = (_ip1) >> 24; \ - ed->i13 = (_ip1) >> 16; \ - ed->i12 = (_ip1) >> 8; \ - ed->i11 = (_ip1); \ - ed->i24 = (_ip2) >> 24; \ - ed->i23 = (_ip2) >> 16; \ - ed->i22 = (_ip2) >> 8; \ - ed->i21 = (_ip2); \ - } \ -} while (0) - #define ikev2_elog_error(_msg) \ _ikev2_elog(IKEV2_LOG_ERROR, "[error] " _msg) #define ikev2_elog_warning(_msg) \ @@ -258,19 +244,19 @@ typedef struct typedef struct { - u8 ts_type; + ikev2_traffic_selector_type_t ts_type; u8 protocol_id; u16 selector_len; u16 start_port; u16 end_port; - ip4_address_t start_addr; - ip4_address_t end_addr; + ip_address_t start_addr; + ip_address_t end_addr; } ikev2_ts_t; typedef struct { u32 sw_if_index; - ip4_address_t ip4; + ip_address_t addr; } ikev2_responder_t; typedef struct @@ -361,15 +347,31 @@ typedef struct u32 tun_itf; u8 udp_encap; + u8 natt_disabled; } ikev2_profile_t; +typedef enum +{ + /* SA will switch to port 4500 when NAT is detected. + * This is the default. */ + IKEV2_NATT_ENABLED, + + /* Do nothing when NAT is detected */ + IKEV2_NATT_DISABLED, + + /* NAT was detected and port switched to 4500 */ + IKEV2_NATT_ACTIVE, +} ikev2_natt_state_t; + +#define ikev2_natt_active(_sa) ((_sa)->natt_state == IKEV2_NATT_ACTIVE) + typedef struct { ikev2_state_t state; u8 unsupported_cp; u8 initial_contact; - ip4_address_t iaddr; - ip4_address_t raddr; + ip_address_t iaddr; + ip_address_t raddr; u64 ispi; u64 rspi; u8 *i_nonce; @@ -414,10 +416,12 @@ typedef struct u8 *last_sa_init_res_packet_data; /* retransmit */ + /* message id expected in the request from the other peer */ u32 last_msg_id; u8 *last_res_packet_data; u8 is_initiator; + /* last message id that was used for an initiated request */ u32 last_init_msg_id; u32 profile_index; u8 is_tun_itf_set; @@ -440,7 +444,7 @@ typedef struct u32 sw_if_index; /* is NAT traversal mode */ - u8 natt; + ikev2_natt_state_t natt_state; u8 keys_generated; } ikev2_sa_t; @@ -508,6 +512,9 @@ typedef struct /* max number of retries before considering peer dead */ u32 liveness_max_retries; + + /* dead peer detection */ + u8 dpd_disabled; } ikev2_main_t; extern ikev2_main_t ikev2_main; @@ -522,18 +529,19 @@ u8 *ikev2_calc_prfplus (ikev2_sa_transform_t * tr, u8 * key, u8 * seed, int len); v8 *ikev2_calc_integr (ikev2_sa_transform_t * tr, v8 * key, u8 * data, int len); -v8 *ikev2_decrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, - ikev2_sa_transform_t * tr_encr, u8 * data, int len); +int ikev2_decrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, + ikev2_sa_transform_t * tr_encr, u8 * data, int len, + u32 * out_len); int ikev2_encrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, ikev2_sa_transform_t * tr_encr, v8 * src, u8 * dst); int ikev2_encrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, ikev2_sa_transform_t * tr_encr, v8 * src, u8 * dst, u8 * aad, u32 aad_len, u8 * tag); -u8 *ikev2_decrypt_aead_data (ikev2_main_per_thread_data_t * ptd, +int ikev2_decrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, ikev2_sa_transform_t * tr_encr, u8 * data, int data_len, u8 * aad, u32 aad_len, - u8 * tag); + u8 * tag, u32 * out_len); void ikev2_generate_dh (ikev2_sa_t * sa, ikev2_sa_transform_t * t); void ikev2_complete_dh (ikev2_sa_t * sa, ikev2_sa_transform_t * t); int ikev2_verify_sign (EVP_PKEY * pkey, u8 * sigbuf, u8 * data); @@ -573,11 +581,17 @@ void ikev2_payload_add_ts (ikev2_payload_chain_t * c, ikev2_ts_t * ts, void ikev2_payload_add_delete (ikev2_payload_chain_t * c, ikev2_delete_t * d); void ikev2_payload_chain_add_padding (ikev2_payload_chain_t * c, int bs); void ikev2_parse_vendor_payload (ike_payload_header_t * ikep); -ikev2_sa_proposal_t *ikev2_parse_sa_payload (ike_payload_header_t * ikep); -ikev2_ts_t *ikev2_parse_ts_payload (ike_payload_header_t * ikep); -ikev2_delete_t *ikev2_parse_delete_payload (ike_payload_header_t * ikep); -ikev2_notify_t *ikev2_parse_notify_payload (ike_payload_header_t * ikep); +ikev2_sa_proposal_t *ikev2_parse_sa_payload (ike_payload_header_t * ikep, + u32 rlen); +ikev2_ts_t *ikev2_parse_ts_payload (ike_payload_header_t * ikep, u32 rlen); +ikev2_delete_t *ikev2_parse_delete_payload (ike_payload_header_t * ikep, + u32 rlen); +ikev2_notify_t *ikev2_parse_notify_payload (ike_payload_header_t * ikep, + u32 rlen); int ikev2_set_log_level (ikev2_log_level_t log_level); +u8 *ikev2_find_ike_notify_payload (ike_header_t * ike, u32 msg_type); +void ikev2_disable_dpd (void); +clib_error_t *ikev2_profile_natt_disable (u8 * name); static_always_inline ikev2_main_per_thread_data_t * ikev2_get_per_thread_data () diff --git a/src/plugins/ikev2/ikev2_test.c b/src/plugins/ikev2/ikev2_test.c index 222f01ad1239..d9f5e2256b6b 100644 --- a/src/plugins/ikev2/ikev2_test.c +++ b/src/plugins/ikev2/ikev2_test.c @@ -46,6 +46,7 @@ typedef struct vat_main_t *vat_main; } ikev2_test_main_t; +static const char *valid_chars = "a-zA-Z0-9_"; ikev2_test_main_t ikev2_test_main; uword @@ -135,7 +136,7 @@ MACRO_FORMAT (auth_method) s = format (s, " %s", id->data); break; case IKEV2_ID_TYPE_ID_IPV4_ADDR: - s = format (s, " %U", format_ip4_address, id->data); + s = format (s, " %U", format_ip_address, id->data); break; case IKEV2_ID_TYPE_ID_KEY_ID: s = format (s, " 0x%U", format_hex_bytes, id->data, id->data_len); @@ -191,6 +192,47 @@ format_ikev2_sa_transform (u8 * s, va_list * args) return s; } +static int +api_ikev2_profile_disable_natt (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_ikev2_profile_disable_natt_t *mp; + u8 *name = 0; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_token, valid_chars, &name)) + vec_add1 (name, 0); + else + { + errmsg ("parse error '%U'", format_unformat_error, i); + return -99; + } + } + + if (!vec_len (name)) + { + errmsg ("profile name must be specified"); + return -99; + } + + if (vec_len (name) > 64) + { + errmsg ("profile name too long"); + return -99; + } + + M (IKEV2_PROFILE_DISABLE_NATT, mp); + + clib_memcpy (mp->name, name, vec_len (name)); + vec_free (name); + + S (mp); + W (ret); + return ret; +} + static int api_ikev2_profile_dump (vat_main_t * vam) { @@ -225,7 +267,6 @@ static void vl_api_ikev2_profile_details_t_handler { vat_main_t *vam = ikev2_test_main.vat_main; vl_api_ikev2_profile_t *p = &mp->profile; - ip4_address_t start_addr, end_addr; fformat (vam->ofp, "profile %s\n", p->name); @@ -256,21 +297,17 @@ static void vl_api_ikev2_profile_details_t_handler format_ikev2_id_type_and_data, &p->rem_id); } - ip4_address_decode (p->loc_ts.start_addr, &start_addr); - ip4_address_decode (p->loc_ts.end_addr, &end_addr); fformat (vam->ofp, " local traffic-selector addr %U - %U port %u - %u" " protocol %u\n", - format_ip4_address, &start_addr, - format_ip4_address, &end_addr, + format_ip_address, &p->loc_ts.start_addr, + format_ip_address, &p->loc_ts.end_addr, clib_net_to_host_u16 (p->loc_ts.start_port), clib_net_to_host_u16 (p->loc_ts.end_port), p->loc_ts.protocol_id); - ip4_address_decode (p->rem_ts.start_addr, &start_addr); - ip4_address_decode (p->rem_ts.end_addr, &end_addr); fformat (vam->ofp, " remote traffic-selector addr %U - %U port %u - %u" " protocol %u\n", - format_ip4_address, &start_addr, - format_ip4_address, &end_addr, + format_ip_address, &p->rem_ts.start_addr, + format_ip_address, &p->rem_ts.end_addr, clib_net_to_host_u16 (p->rem_ts.start_port), clib_net_to_host_u16 (p->rem_ts.end_port), p->rem_ts.protocol_id); u32 tun_itf = clib_net_to_host_u32 (p->tun_itf); @@ -280,11 +317,14 @@ static void vl_api_ikev2_profile_details_t_handler u32 sw_if_index = clib_net_to_host_u32 (p->responder.sw_if_index); if (~0 != sw_if_index) fformat (vam->ofp, " responder idx %d %U\n", - sw_if_index, format_ip4_address, &p->responder.ip4); + sw_if_index, format_ip_address, &p->responder.addr); if (p->udp_encap) fformat (vam->ofp, " udp-encap\n"); + if (p->natt_disabled) + fformat (vam->ofp, " NAT-T disabled\n"); + u32 ipsec_over_udp_port = clib_net_to_host_u16 (p->ipsec_over_udp_port); if (ipsec_over_udp_port != IPSEC_UDP_PORT_NONE) fformat (vam->ofp, " ipsec-over-udp port %d\n", ipsec_over_udp_port); @@ -348,18 +388,18 @@ vl_api_ikev2_sa_details_t_handler (vl_api_ikev2_sa_details_t * mp) { vat_main_t *vam = ikev2_test_main.vat_main; vl_api_ikev2_sa_t *sa = &mp->sa; - ip4_address_t iaddr; - ip4_address_t raddr; + ip_address_t iaddr; + ip_address_t raddr; vl_api_ikev2_keys_t *k = &sa->keys; vl_api_ikev2_sa_t_endian (sa); - ip4_address_decode (sa->iaddr, &iaddr); - ip4_address_decode (sa->raddr, &raddr); + ip_address_decode2 (&sa->iaddr, &iaddr); + ip_address_decode2 (&sa->raddr, &raddr); fformat (vam->ofp, "profile index %d sa index: %d\n", mp->sa.profile_index, mp->sa.sa_index); - fformat (vam->ofp, " iip %U ispi %lx rip %U rspi %lx\n", format_ip4_address, - &iaddr, sa->ispi, format_ip4_address, &raddr, sa->rspi); + fformat (vam->ofp, " iip %U ispi %lx rip %U rspi %lx\n", format_ip_address, + &iaddr, sa->ispi, format_ip_address, &raddr, sa->rspi); fformat (vam->ofp, " %U ", format_ikev2_sa_transform, &sa->encryption); fformat (vam->ofp, "%U ", format_ikev2_sa_transform, &sa->prf); fformat (vam->ofp, "%U ", format_ikev2_sa_transform, &sa->integrity); @@ -526,18 +566,17 @@ static void { vat_main_t *vam = ikev2_test_main.vat_main; vl_api_ikev2_ts_t *ts = &mp->ts; - ip4_address_t start_addr; - ip4_address_t end_addr; + ip_address_t start_addr, end_addr; vl_api_ikev2_ts_t_endian (ts); - ip4_address_decode (ts->start_addr, &start_addr); - ip4_address_decode (ts->end_addr, &end_addr); + ip_address_decode2 (&ts->start_addr, &start_addr); + ip_address_decode2 (&ts->end_addr, &end_addr); fformat (vam->ofp, " %s protocol_id %u addr " "%U - %U port %u - %u\n", ts->is_local, ts->protocol_id, - format_ip4_address, &start_addr, - format_ip4_address, &end_addr, ts->start_port, ts->end_port); + format_ip_address, &start_addr, + format_ip_address, &end_addr, ts->start_port, ts->end_port); vam->result_ready = 1; } @@ -680,8 +719,6 @@ api_ikev2_profile_add_del (vat_main_t * vam) u8 *name = 0; int ret; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "del")) @@ -729,8 +766,6 @@ api_ikev2_profile_set_auth (vat_main_t * vam) u8 is_hex = 0; int ret; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "name %U", unformat_token, valid_chars, &name)) @@ -797,21 +832,19 @@ api_ikev2_profile_set_id (vat_main_t * vam) u8 *data = 0; u8 is_local = 0; u32 id_type = 0; - ip4_address_t ip4; + ip_address_t ip; int ret; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "name %U", unformat_token, valid_chars, &name)) vec_add1 (name, 0); else if (unformat (i, "id_type %U", unformat_ikev2_id_type, &id_type)) ; - else if (unformat (i, "id_data %U", unformat_ip4_address, &ip4)) + else if (unformat (i, "id_data %U", unformat_ip_address, &ip)) { - data = vec_new (u8, 4); - clib_memcpy (data, ip4.as_u8, 4); + data = vec_new (u8, ip_address_size (&ip)); + clib_memcpy (data, ip_addr_bytes (&ip), ip_address_size (&ip)); } else if (unformat (i, "id_data 0x%U", unformat_hex_string, &data)) ; @@ -875,14 +908,10 @@ api_ikev2_profile_set_ts (vat_main_t * vam) u8 *name = 0; u8 is_local = 0; u32 proto = 0, start_port = 0, end_port = (u32) ~ 0; - ip4_address_t start_addr, end_addr; - - const char *valid_chars = "a-zA-Z0-9_"; + ip_address_t start_addr, end_addr; + u8 start_addr_set = 0, end_addr_set = 0; int ret; - start_addr.as_u32 = 0; - end_addr.as_u32 = (u32) ~ 0; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "name %U", unformat_token, valid_chars, &name)) @@ -894,10 +923,10 @@ api_ikev2_profile_set_ts (vat_main_t * vam) else if (unformat (i, "end_port %d", &end_port)) ; else - if (unformat (i, "start_addr %U", unformat_ip4_address, &start_addr)) - ; - else if (unformat (i, "end_addr %U", unformat_ip4_address, &end_addr)) - ; + if (unformat (i, "start_addr %U", unformat_ip_address, &start_addr)) + start_addr_set = 1; + else if (unformat (i, "end_addr %U", unformat_ip_address, &end_addr)) + end_addr_set = 1; else if (unformat (i, "local")) is_local = 1; else if (unformat (i, "remote")) @@ -909,6 +938,12 @@ api_ikev2_profile_set_ts (vat_main_t * vam) } } + if (!start_addr_set || !end_addr_set) + { + errmsg ("missing start or end address"); + return -99; + } + if (!vec_len (name)) { errmsg ("profile name must be specified"); @@ -927,8 +962,8 @@ api_ikev2_profile_set_ts (vat_main_t * vam) mp->ts.protocol_id = (u8) proto; mp->ts.start_port = clib_host_to_net_u16 ((u16) start_port); mp->ts.end_port = clib_host_to_net_u16 ((u16) end_port); - ip4_address_encode (&start_addr, mp->ts.start_addr); - ip4_address_encode (&end_addr, mp->ts.end_addr); + ip_address_encode2 (&start_addr, &mp->ts.start_addr); + ip_address_encode2 (&end_addr, &mp->ts.end_addr); clib_memcpy (mp->name, name, vec_len (name)); vec_free (name); @@ -986,8 +1021,6 @@ api_ikev2_profile_set_udp_encap (vat_main_t * vam) int ret; u8 *name = 0; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U udp-encap", unformat_token, valid_chars, &name)) @@ -1035,15 +1068,13 @@ api_ikev2_set_responder (vat_main_t * vam) int ret; u8 *name = 0; u32 sw_if_index = ~0; - ip4_address_t address; - - const char *valid_chars = "a-zA-Z0-9_"; + ip_address_t address; while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U interface %d address %U", unformat_token, valid_chars, - &name, &sw_if_index, unformat_ip4_address, &address)) + &name, &sw_if_index, unformat_ip_address, &address)) vec_add1 (name, 0); else { @@ -1070,7 +1101,7 @@ api_ikev2_set_responder (vat_main_t * vam) vec_free (name); mp->responder.sw_if_index = clib_host_to_net_u32 (sw_if_index); - ip4_address_encode (&address, mp->responder.ip4); + ip_address_encode2 (&address, &mp->responder.addr); S (mp); W (ret); @@ -1086,8 +1117,6 @@ api_ikev2_set_ike_transforms (vat_main_t * vam) u8 *name = 0; u32 crypto_alg, crypto_key_size, integ_alg, dh_group; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U %d %d %d %d", unformat_token, valid_chars, &name, @@ -1136,8 +1165,6 @@ api_ikev2_set_esp_transforms (vat_main_t * vam) u8 *name = 0; u32 crypto_alg, crypto_key_size, integ_alg; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U %d %d %d", unformat_token, valid_chars, &name, @@ -1185,8 +1212,6 @@ api_ikev2_set_sa_lifetime (vat_main_t * vam) u64 lifetime, lifetime_maxdata; u32 lifetime_jitter, handover; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U %lu %u %u %lu", unformat_token, valid_chars, &name, @@ -1234,8 +1259,6 @@ api_ikev2_initiate_sa_init (vat_main_t * vam) int ret; u8 *name = 0; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U", unformat_token, valid_chars, &name)) diff --git a/src/plugins/ikev2/ikev2_types.api b/src/plugins/ikev2/ikev2_types.api index 58297c05e9ca..d39cf88c8d4f 100644 --- a/src/plugins/ikev2/ikev2_types.api +++ b/src/plugins/ikev2/ikev2_types.api @@ -34,8 +34,8 @@ typedef ikev2_ts u8 protocol_id; u16 start_port; u16 end_port; - vl_api_ip4_address_t start_addr; - vl_api_ip4_address_t end_addr; + vl_api_address_t start_addr; + vl_api_address_t end_addr; }; typedef ikev2_auth @@ -49,7 +49,7 @@ typedef ikev2_auth typedef ikev2_responder { vl_api_interface_index_t sw_if_index; - vl_api_ip4_address_t ip4; + vl_api_address_t addr; }; typedef ikev2_ike_transforms @@ -84,6 +84,7 @@ typedef ikev2_profile u16 ipsec_over_udp_port; u32 tun_itf; bool udp_encap; + bool natt_disabled; vl_api_ikev2_auth_t auth; }; @@ -134,8 +135,8 @@ typedef ikev2_sa u64 ispi; u64 rspi; - vl_api_ip4_address_t iaddr; - vl_api_ip4_address_t raddr; + vl_api_address_t iaddr; + vl_api_address_t raddr; vl_api_ikev2_keys_t keys; diff --git a/src/plugins/ikev2/test/test_ikev2.py b/src/plugins/ikev2/test/test_ikev2.py index 6116ebb29a73..e4471dd843a6 100644 --- a/src/plugins/ikev2/test/test_ikev2.py +++ b/src/plugins/ikev2/test/test_ikev2.py @@ -1,4 +1,6 @@ import os +import time +from socket import inet_pton from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, hmac @@ -9,15 +11,20 @@ algorithms, modes, ) -from ipaddress import IPv4Address +from ipaddress import IPv4Address, IPv6Address, ip_address from scapy.layers.ipsec import ESP from scapy.layers.inet import IP, UDP, Ether +from scapy.layers.inet6 import IPv6 from scapy.packet import raw, Raw from scapy.utils import long_converter from framework import VppTestCase, VppTestRunner from vpp_ikev2 import Profile, IDType, AuthMethod from vpp_papi import VppEnum +try: + text_type = unicode +except NameError: + text_type = str KEY_PAD = b"Key Pad for IKEv2" SALT_SIZE = 4 @@ -107,14 +114,12 @@ def decrypt(self, data, key, aad=None, icv=None): self.mode(nonce, icv, len(icv)), default_backend()).decryptor() decryptor.authenticate_additional_data(aad) - pt = decryptor.update(ct) + decryptor.finalize() - pad_len = pt[-1] + 1 - return pt[:-pad_len] + return decryptor.update(ct) + decryptor.finalize() def pad(self, data): pad_len = (len(data) // self.bs + 1) * self.bs - len(data) data = data + b'\x00' * (pad_len - 1) - return data + bytes([pad_len]) + return data + bytes([pad_len - 1]) class AuthAlgo(object): @@ -147,26 +152,48 @@ def __init__(self, name, mac, mod, key_len, trunc_len=None): hashes.SHA256, 32), } +CRYPTO_IDS = { + 12: 'AES-CBC', + 20: 'AES-GCM-16ICV', +} + +INTEG_IDS = { + 2: 'HMAC-SHA1-96', + 12: 'SHA2-256-128', + 13: 'SHA2-384-192', + 14: 'SHA2-512-256', +} + class IKEv2ChildSA(object): - def __init__(self, local_ts, remote_ts, spi=None): - self.spi = spi or os.urandom(4) + def __init__(self, local_ts, remote_ts, is_initiator): + spi = os.urandom(4) + if is_initiator: + self.ispi = spi + self.rspi = None + else: + self.rspi = spi + self.ispi = None self.local_ts = local_ts self.remote_ts = remote_ts class IKEv2SA(object): - def __init__(self, test, is_initiator=True, spi=b'\x04' * 8, - i_id=None, r_id=None, id_type='fqdn', nonce=None, - auth_data=None, local_ts=None, remote_ts=None, - auth_method='shared-key', priv_key=None, natt=False): - self.natt = natt - if natt: + def __init__(self, test, is_initiator=True, i_id=None, r_id=None, + spi=b'\x01\x02\x03\x04\x05\x06\x07\x08', id_type='fqdn', + nonce=None, auth_data=None, local_ts=None, remote_ts=None, + auth_method='shared-key', priv_key=None, i_natt=False, + r_natt=False, udp_encap=False): + self.udp_encap = udp_encap + self.i_natt = i_natt + self.r_natt = r_natt + if i_natt or r_natt: self.sport = 4500 self.dport = 4500 else: self.sport = 500 self.dport = 500 + self.msg_id = 0 self.dh_params = None self.test = test self.priv_key = priv_key @@ -181,40 +208,63 @@ def __init__(self, test, is_initiator=True, spi=b'\x04' * 8, self.id_type = id_type self.auth_method = auth_method if self.is_initiator: - self.rspi = None + self.rspi = 8 * b'\x00' self.ispi = spi self.i_nonce = nonce else: self.rspi = spi - self.ispi = None - self.r_nonce = None - self.child_sas = [IKEv2ChildSA(local_ts, remote_ts)] + self.ispi = 8 * b'\x00' + self.r_nonce = nonce + self.child_sas = [IKEv2ChildSA(local_ts, remote_ts, + self.is_initiator)] - def dh_pub_key(self): + def new_msg_id(self): + self.msg_id += 1 + return self.msg_id + + @property + def my_dh_pub_key(self): + if self.is_initiator: + return self.i_dh_data + return self.r_dh_data + + @property + def peer_dh_pub_key(self): + if self.is_initiator: + return self.r_dh_data return self.i_dh_data + @property + def natt(self): + return self.i_natt or self.r_natt + def compute_secret(self): priv = self.dh_private_key - peer = self.r_dh_data + peer = self.peer_dh_pub_key p, g, l = self.ike_group return pow(int.from_bytes(peer, 'big'), int.from_bytes(priv, 'big'), p).to_bytes(l, 'big') def generate_dh_data(self): # generate DH keys + if self.ike_dh not in DH: + raise NotImplementedError('%s not in DH group' % self.ike_dh) + + if self.dh_params is None: + dhg = DH[self.ike_dh] + pn = dh.DHParameterNumbers(dhg[0], dhg[1]) + self.dh_params = pn.parameters(default_backend()) + + priv = self.dh_params.generate_private_key() + pub = priv.public_key() + x = priv.private_numbers().x + self.dh_private_key = x.to_bytes(priv.key_size // 8, 'big') + y = pub.public_numbers().y + if self.is_initiator: - if self.ike_dh not in DH: - raise NotImplementedError('%s not in DH group' % self.ike_dh) - if self.dh_params is None: - dhg = DH[self.ike_dh] - pn = dh.DHParameterNumbers(dhg[0], dhg[1]) - self.dh_params = pn.parameters(default_backend()) - priv = self.dh_params.generate_private_key() - pub = priv.public_key() - x = priv.private_numbers().x - self.dh_private_key = x.to_bytes(priv.key_size // 8, 'big') - y = pub.public_numbers().y self.i_dh_data = y.to_bytes(pub.key_size // 8, 'big') + else: + self.r_dh_data = y.to_bytes(pub.key_size // 8, 'big') def complete_dh_data(self): self.dh_shared_secret = self.compute_secret() @@ -325,13 +375,21 @@ def generate_authmsg(self, prf, packet): id = self.i_id nonce = self.r_nonce key = self.sk_pi + else: + id = self.r_id + nonce = self.i_nonce + key = self.sk_pr data = bytes([self.id_type, 0, 0, 0]) + id id_hash = self.calc_prf(prf, key, data) return packet + nonce + id_hash def auth_init(self): prf = self.ike_prf_alg.mod() - authmsg = self.generate_authmsg(prf, raw(self.init_req_packet)) + if self.is_initiator: + packet = self.init_req_packet + else: + packet = self.init_resp_packet + authmsg = self.generate_authmsg(prf, raw(packet)) if self.auth_method == 'shared-key': psk = self.calc_prf(prf, self.auth_data, KEY_PAD) self.auth_data = self.calc_prf(prf, psk, authmsg) @@ -402,28 +460,41 @@ def hmac_and_decrypt(self, ike): aad_len = len(ikev2.IKEv2_payload_Encrypted()) + len(ikev2.IKEv2()) ct = ep.load[:-GCM_ICV_SIZE] tag = ep.load[-GCM_ICV_SIZE:] - return self.decrypt(ct, raw(ike)[:aad_len], tag) + plain = self.decrypt(ct, raw(ike)[:aad_len], tag) else: self.verify_hmac(raw(ike)) integ_trunc = self.ike_integ_alg.trunc_len # remove ICV and decrypt payload ct = ep.load[:-integ_trunc] - return self.decrypt(ct) + plain = self.decrypt(ct) + # remove padding + pad_len = plain[-1] + return plain[:-pad_len - 1] + + def build_ts_addr(self, ts, version): + return {'starting_address_v' + version: ts['start_addr'], + 'ending_address_v' + version: ts['end_addr']} - def generate_ts(self): + def generate_ts(self, is_ip4): c = self.child_sas[0] - ts1 = ikev2.IPv4TrafficSelector( - IP_protocol_ID=0, - start_port=0, - end_port=0xffff, - starting_address_v4=c.local_ts['start_addr'], - ending_address_v4=c.local_ts['end_addr']) - ts2 = ikev2.IPv4TrafficSelector( - IP_protocol_ID=0, - starting_address_v4=c.remote_ts['start_addr'], - ending_address_v4=c.remote_ts['end_addr']) - return ([ts1], [ts2]) + ts_data = {'IP_protocol_ID': 0, + 'start_port': 0, + 'end_port': 0xffff} + if is_ip4: + ts_data.update(self.build_ts_addr(c.local_ts, '4')) + ts1 = ikev2.IPv4TrafficSelector(**ts_data) + ts_data.update(self.build_ts_addr(c.remote_ts, '4')) + ts2 = ikev2.IPv4TrafficSelector(**ts_data) + else: + ts_data.update(self.build_ts_addr(c.local_ts, '6')) + ts1 = ikev2.IPv6TrafficSelector(**ts_data) + ts_data.update(self.build_ts_addr(c.remote_ts, '6')) + ts2 = ikev2.IPv6TrafficSelector(**ts_data) + + if self.is_initiator: + return ([ts1], [ts2]) + return ([ts2], [ts1]) def set_ike_props(self, crypto, crypto_key_len, integ, prf, dh): if crypto not in CRYPTO_ALGOS: @@ -468,133 +539,125 @@ def ike_crypto_attr(self): def esp_crypto_attr(self): return self.crypto_attr(self.esp_crypto_key_len) - def compute_nat_sha1(self, ip, port): - data = self.ispi + b'\x00' * 8 + ip + (port).to_bytes(2, 'big') + def compute_nat_sha1(self, ip, port, rspi=None): + if rspi is None: + rspi = self.rspi + data = self.ispi + rspi + ip + (port).to_bytes(2, 'big') digest = hashes.Hash(hashes.SHA1(), backend=default_backend()) digest.update(data) return digest.finalize() -class TemplateResponder(VppTestCase): - """ responder test template """ +class IkePeer(VppTestCase): + """ common class for initiator and responder """ @classmethod def setUpClass(cls): import scapy.contrib.ikev2 as _ikev2 globals()['ikev2'] = _ikev2 - super(TemplateResponder, cls).setUpClass() + super(IkePeer, cls).setUpClass() cls.create_pg_interfaces(range(2)) for i in cls.pg_interfaces: i.admin_up() i.config_ip4() i.resolve_arp() + i.config_ip6() + i.resolve_ndp() @classmethod def tearDownClass(cls): - super(TemplateResponder, cls).tearDownClass() - - def setUp(self): - super(TemplateResponder, self).setUp() - self.config_tc() - self.p.add_vpp_config() - self.assertIsNotNone(self.p.query_vpp_config()) - self.sa.generate_dh_data() + super(IkePeer, cls).tearDownClass() def tearDown(self): - super(TemplateResponder, self).tearDown() + super(IkePeer, self).tearDown() + if self.del_sa_from_responder: + self.initiate_del_sa_from_responder() + else: + self.initiate_del_sa_from_initiator() + r = self.vapi.ikev2_sa_dump() + self.assertEqual(len(r), 0) + sas = self.vapi.ipsec_sa_dump() + self.assertEqual(len(sas), 0) self.p.remove_vpp_config() self.assertIsNone(self.p.query_vpp_config()) - def create_ike_msg(self, src_if, msg, sport=500, dport=500, natt=False): + def setUp(self): + super(IkePeer, self).setUp() + self.config_tc() + self.p.add_vpp_config() + self.assertIsNotNone(self.p.query_vpp_config()) + if self.sa.is_initiator: + self.sa.generate_dh_data() + self.vapi.cli('ikev2 set logging level 4') + self.vapi.cli('event-lo clear') + + def create_rekey_request(self): + sa, first_payload = self.generate_auth_payload(is_rekey=True) + header = ikev2.IKEv2( + init_SPI=self.sa.ispi, + resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(), + flags='Initiator', exch_type='CREATE_CHILD_SA') + + ike_msg = self.encrypt_ike_msg(header, sa, first_payload) + return self.create_packet(self.pg0, ike_msg, self.sa.sport, + self.sa.dport, self.sa.natt, self.ip6) + + def create_empty_request(self): + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + id=self.sa.new_msg_id(), flags='Initiator', + exch_type='INFORMATIONAL', + next_payload='Encrypted') + + msg = self.encrypt_ike_msg(header, b'', None) + return self.create_packet(self.pg0, msg, self.sa.sport, + self.sa.dport, self.sa.natt, self.ip6) + + def create_packet(self, src_if, msg, sport=500, dport=500, natt=False, + use_ip6=False): + if use_ip6: + src_ip = src_if.remote_ip6 + dst_ip = src_if.local_ip6 + ip_layer = IPv6 + else: + src_ip = src_if.remote_ip4 + dst_ip = src_if.local_ip4 + ip_layer = IP res = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / - IP(src=src_if.remote_ip4, dst=src_if.local_ip4) / + ip_layer(src=src_ip, dst=dst_ip) / UDP(sport=sport, dport=dport)) if natt: # insert non ESP marker res = res / Raw(b'\x00' * 4) return res / msg - def send_sa_init(self, behind_nat=False): - tr_attr = self.sa.ike_crypto_attr() - trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption', - transform_id=self.sa.ike_crypto, length=tr_attr[1], - key_length=tr_attr[0]) / - ikev2.IKEv2_payload_Transform(transform_type='Integrity', - transform_id=self.sa.ike_integ) / - ikev2.IKEv2_payload_Transform(transform_type='PRF', - transform_id=self.sa.ike_prf_alg.name) / - ikev2.IKEv2_payload_Transform(transform_type='GroupDesc', - transform_id=self.sa.ike_dh)) + def verify_udp(self, udp): + self.assertEqual(udp.sport, self.sa.sport) + self.assertEqual(udp.dport, self.sa.dport) - props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='IKEv2', - trans_nb=4, trans=trans)) + def get_ike_header(self, packet): + try: + ih = packet[ikev2.IKEv2] + ih = self.verify_and_remove_non_esp_marker(ih) + except IndexError as e: + # this is a workaround for getting IKEv2 layer as both ikev2 and + # ipsec register for port 4500 + esp = packet[ESP] + ih = self.verify_and_remove_non_esp_marker(esp) + self.assertEqual(ih.version, 0x20) + self.assertNotIn('Version', ih.flags) + return ih - if behind_nat: - next_payload = 'Notify' + def verify_and_remove_non_esp_marker(self, packet): + if self.sa.natt: + # if we are in nat traversal mode check for non esp marker + # and remove it + data = raw(packet) + self.assertEqual(data[:4], b'\x00' * 4) + return ikev2.IKEv2(data[4:]) else: - next_payload = None - - self.sa.init_req_packet = ( - ikev2.IKEv2(init_SPI=self.sa.ispi, - flags='Initiator', exch_type='IKE_SA_INIT') / - ikev2.IKEv2_payload_SA(next_payload='KE', prop=props) / - ikev2.IKEv2_payload_KE(next_payload='Nonce', - group=self.sa.ike_dh, - load=self.sa.dh_pub_key()) / - ikev2.IKEv2_payload_Nonce(next_payload=next_payload, - load=self.sa.i_nonce)) - - if behind_nat: - src_nat = self.sa.compute_nat_sha1(b'\x0a\x0a\x0a\x01', - self.sa.sport) - nat_detection = ikev2.IKEv2_payload_Notify( - type='NAT_DETECTION_SOURCE_IP', - load=src_nat) - self.sa.init_req_packet = self.sa.init_req_packet / nat_detection - - ike_msg = self.create_ike_msg(self.pg0, self.sa.init_req_packet, - self.sa.sport, self.sa.dport, - self.sa.natt) - self.pg0.add_stream(ike_msg) - self.pg0.enable_capture() - self.pg_start() - capture = self.pg0.get_capture(1) - self.verify_sa_init(capture[0]) - - def send_sa_auth(self): - tr_attr = self.sa.esp_crypto_attr() - trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption', - transform_id=self.sa.esp_crypto, length=tr_attr[1], - key_length=tr_attr[0]) / - ikev2.IKEv2_payload_Transform(transform_type='Integrity', - transform_id=self.sa.esp_integ) / - ikev2.IKEv2_payload_Transform( - transform_type='Extended Sequence Number', - transform_id='No ESN') / - ikev2.IKEv2_payload_Transform( - transform_type='Extended Sequence Number', - transform_id='ESN')) - - props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP', - SPIsize=4, SPI=os.urandom(4), trans_nb=4, trans=trans)) - - tsi, tsr = self.sa.generate_ts() - plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr', - IDtype=self.sa.id_type, load=self.sa.i_id) / - ikev2.IKEv2_payload_IDr(next_payload='AUTH', - IDtype=self.sa.id_type, load=self.sa.r_id) / - ikev2.IKEv2_payload_AUTH(next_payload='SA', - auth_type=AuthMethod.value(self.sa.auth_method), - load=self.sa.auth_data) / - ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) / - ikev2.IKEv2_payload_TSi(next_payload='TSr', - number_of_TSs=len(tsi), - traffic_selector=tsi) / - ikev2.IKEv2_payload_TSr(next_payload='Notify', - number_of_TSs=len(tsr), - traffic_selector=tsr) / - ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT')) + return packet + def encrypt_ike_msg(self, header, plain, first_payload): if self.sa.ike_crypto == 'AES-GCM-16ICV': data = self.sa.ike_crypto_alg.pad(raw(plain)) plen = len(data) + GCM_IV_SIZE + GCM_ICV_SIZE +\ @@ -602,105 +665,67 @@ def send_sa_auth(self): tlen = plen + len(ikev2.IKEv2()) # prepare aad data - sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi', + sk_p = ikev2.IKEv2_payload_Encrypted(next_payload=first_payload, length=plen) - sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi, - resp_SPI=self.sa.rspi, id=1, - length=tlen, flags='Initiator', exch_type='IKE_AUTH')) - sa_auth /= sk_p - - encr = self.sa.encrypt(raw(plain), raw(sa_auth)) - sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi', + header.length = tlen + res = header / sk_p + encr = self.sa.encrypt(raw(plain), raw(res)) + sk_p = ikev2.IKEv2_payload_Encrypted(next_payload=first_payload, length=plen, load=encr) - sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi, - resp_SPI=self.sa.rspi, id=1, - length=tlen, flags='Initiator', exch_type='IKE_AUTH')) - sa_auth /= sk_p + res = header / sk_p else: encr = self.sa.encrypt(raw(plain)) trunc_len = self.sa.ike_integ_alg.trunc_len plen = len(encr) + len(ikev2.IKEv2_payload_Encrypted()) + trunc_len tlen = plen + len(ikev2.IKEv2()) - sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi', + sk_p = ikev2.IKEv2_payload_Encrypted(next_payload=first_payload, length=plen, load=encr) - sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi, - resp_SPI=self.sa.rspi, id=1, - length=tlen, flags='Initiator', exch_type='IKE_AUTH')) - sa_auth /= sk_p + header.length = tlen + res = header / sk_p - integ_data = raw(sa_auth) + integ_data = raw(res) hmac_data = self.sa.compute_hmac(self.sa.ike_integ_alg.mod(), self.sa.my_authkey, integ_data) - sa_auth = sa_auth / Raw(hmac_data[:trunc_len]) - - assert(len(sa_auth) == tlen) - packet = self.create_ike_msg(self.pg0, sa_auth, self.sa.sport, - self.sa.dport, self.sa.natt) - self.pg0.add_stream(packet) - self.pg0.enable_capture() - self.pg_start() - capture = self.pg0.get_capture(1) - self.verify_sa_auth(capture[0]) - - def get_ike_header(self, packet): - try: - ih = packet[ikev2.IKEv2] - except IndexError as e: - # this is a workaround for getting IKEv2 layer as both ikev2 and - # ipsec register for port 4500 - esp = packet[ESP] - ih = self.verify_and_remove_non_esp_marker(esp) - return ih - - def verify_sa_init(self, packet): - ih = self.get_ike_header(packet) - - self.assertEqual(ih.exch_type, 34) - self.assertTrue('Response' in ih.flags) - self.assertEqual(ih.init_SPI, self.sa.ispi) - self.assertNotEqual(ih.resp_SPI, 0) - self.sa.rspi = ih.resp_SPI - try: - sa = ih[ikev2.IKEv2_payload_SA] - self.sa.r_nonce = ih[ikev2.IKEv2_payload_Nonce].load - self.sa.r_dh_data = ih[ikev2.IKEv2_payload_KE].load - except IndexError as e: - self.logger.error("unexpected reply: SA/Nonce/KE payload found!") - self.logger.error(ih.show()) - raise - self.sa.complete_dh_data() - self.sa.calc_keys() - self.sa.auth_init() - - def verify_and_remove_non_esp_marker(self, packet): - if self.sa.natt: - # if we are in nat traversal mode check for non esp marker - # and remove it - data = raw(packet) - self.assertEqual(data[:4], b'\x00' * 4) - return ikev2.IKEv2(data[4:]) + res = res / Raw(hmac_data[:trunc_len]) + assert(len(res) == tlen) + return res + + def verify_udp_encap(self, ipsec_sa): + e = VppEnum.vl_api_ipsec_sad_flags_t + if self.sa.udp_encap or self.sa.natt: + self.assertIn(e.IPSEC_API_SAD_FLAG_UDP_ENCAP, ipsec_sa.flags) else: - return packet - - def verify_udp(self, udp): - self.assertEqual(udp.sport, self.sa.sport) - self.assertEqual(udp.dport, self.sa.dport) - - def verify_sa_auth(self, packet): - ike = self.get_ike_header(packet) - udp = packet[UDP] - self.verify_udp(udp) - plain = self.sa.hmac_and_decrypt(ike) - self.sa.calc_child_keys() + self.assertNotIn(e.IPSEC_API_SAD_FLAG_UDP_ENCAP, ipsec_sa.flags) - def verify_ipsec_sas(self): + def verify_ipsec_sas(self, is_rekey=False): sas = self.vapi.ipsec_sa_dump() - self.assertEqual(len(sas), 2) - sa0 = sas[0].entry - sa1 = sas[1].entry + if is_rekey: + # after rekey there is a short period of time in which old + # inbound SA is still present + sa_count = 3 + else: + sa_count = 2 + self.assertEqual(len(sas), sa_count) + if self.sa.is_initiator: + if is_rekey: + sa0 = sas[0].entry + sa1 = sas[2].entry + else: + sa0 = sas[0].entry + sa1 = sas[1].entry + else: + if is_rekey: + sa0 = sas[2].entry + sa1 = sas[0].entry + else: + sa1 = sas[0].entry + sa0 = sas[1].entry + c = self.sa.child_sas[0] + self.verify_udp_encap(sa0) + self.verify_udp_encap(sa1) vpp_crypto_alg = self.vpp_enums[self.sa.vpp_esp_cypto_alg] self.assertEqual(sa0.crypto_algorithm, vpp_crypto_alg) self.assertEqual(sa1.crypto_algorithm, vpp_crypto_alg) @@ -744,10 +769,22 @@ def verify_ike_sas(self): r = self.vapi.ikev2_sa_dump() self.assertEqual(len(r), 1) sa = r[0].sa - self.assertEqual(self.sa.ispi, (sa.ispi).to_bytes(8, 'little')) + self.assertEqual(self.sa.ispi, (sa.ispi).to_bytes(8, 'big')) self.assertEqual(self.sa.rspi, (sa.rspi).to_bytes(8, 'big')) - self.assertEqual(sa.iaddr, IPv4Address(self.pg0.remote_ip4)) - self.assertEqual(sa.raddr, IPv4Address(self.pg0.local_ip4)) + if self.ip6: + if self.sa.is_initiator: + self.assertEqual(sa.iaddr, IPv6Address(self.pg0.remote_ip6)) + self.assertEqual(sa.raddr, IPv6Address(self.pg0.local_ip6)) + else: + self.assertEqual(sa.iaddr, IPv6Address(self.pg0.local_ip6)) + self.assertEqual(sa.raddr, IPv6Address(self.pg0.remote_ip6)) + else: + if self.sa.is_initiator: + self.assertEqual(sa.iaddr, IPv4Address(self.pg0.remote_ip4)) + self.assertEqual(sa.raddr, IPv4Address(self.pg0.local_ip4)) + else: + self.assertEqual(sa.iaddr, IPv4Address(self.pg0.local_ip4)) + self.assertEqual(sa.raddr, IPv4Address(self.pg0.remote_ip4)) self.verify_keymat(sa.keys, self.sa, 'sk_d') self.verify_keymat(sa.keys, self.sa, 'sk_ai') self.verify_keymat(sa.keys, self.sa, 'sk_ar') @@ -773,8 +810,10 @@ def verify_ike_sas(self): self.verify_keymat(csa.keys, c, 'sk_ar') self.verify_keymat(csa.keys, c, 'sk_ei') self.verify_keymat(csa.keys, c, 'sk_er') + self.assertEqual(csa.i_spi.to_bytes(4, 'big'), c.ispi) + self.assertEqual(csa.r_spi.to_bytes(4, 'big'), c.rspi) - tsi, tsr = self.sa.generate_ts() + tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4) tsi = tsi[0] tsr = tsr[0] r = self.vapi.ikev2_traffic_selector_dump( @@ -806,16 +845,479 @@ def verify_ts(self, api_ts, ts, is_initiator): self.assertTrue(api_ts.is_local) else: self.assertFalse(api_ts.is_local) - self.assertEqual(api_ts.start_addr, - IPv4Address(ts.starting_address_v4)) - self.assertEqual(api_ts.end_addr, - IPv4Address(ts.ending_address_v4)) + + if self.p.ts_is_ip4: + self.assertEqual(api_ts.start_addr, + IPv4Address(ts.starting_address_v4)) + self.assertEqual(api_ts.end_addr, + IPv4Address(ts.ending_address_v4)) + else: + self.assertEqual(api_ts.start_addr, + IPv6Address(ts.starting_address_v6)) + self.assertEqual(api_ts.end_addr, + IPv6Address(ts.ending_address_v6)) self.assertEqual(api_ts.start_port, ts.start_port) self.assertEqual(api_ts.end_port, ts.end_port) self.assertEqual(api_ts.protocol_id, ts.IP_protocol_ID) + +class TemplateInitiator(IkePeer): + """ initiator test template """ + + def initiate_del_sa_from_initiator(self): + ispi = int.from_bytes(self.sa.ispi, 'little') + self.pg0.enable_capture() + self.pg_start() + self.vapi.ikev2_initiate_del_ike_sa(ispi=ispi) + capture = self.pg0.get_capture(1) + ih = self.get_ike_header(capture[0]) + self.assertNotIn('Response', ih.flags) + self.assertIn('Initiator', ih.flags) + self.assertEqual(ih.init_SPI, self.sa.ispi) + self.assertEqual(ih.resp_SPI, self.sa.rspi) + plain = self.sa.hmac_and_decrypt(ih) + d = ikev2.IKEv2_payload_Delete(plain) + self.assertEqual(d.proto, 1) # proto=IKEv2 + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + flags='Response', exch_type='INFORMATIONAL', + id=ih.id, next_payload='Encrypted') + resp = self.encrypt_ike_msg(header, b'', None) + self.send_and_assert_no_replies(self.pg0, resp) + + def verify_del_sa(self, packet): + ih = self.get_ike_header(packet) + self.assertEqual(ih.id, self.sa.msg_id) + self.assertEqual(ih.exch_type, 37) # exchange informational + self.assertIn('Response', ih.flags) + self.assertIn('Initiator', ih.flags) + plain = self.sa.hmac_and_decrypt(ih) + self.assertEqual(plain, b'') + + def initiate_del_sa_from_responder(self): + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + exch_type='INFORMATIONAL', + id=self.sa.new_msg_id()) + del_sa = ikev2.IKEv2_payload_Delete(proto='IKEv2') + ike_msg = self.encrypt_ike_msg(header, del_sa, 'Delete') + packet = self.create_packet(self.pg0, ike_msg, + self.sa.sport, self.sa.dport, + self.sa.natt, self.ip6) + self.pg0.add_stream(packet) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + self.verify_del_sa(capture[0]) + + @staticmethod + def find_notify_payload(packet, notify_type): + n = packet[ikev2.IKEv2_payload_Notify] + while n is not None: + if n.type == notify_type: + return n + n = n.payload + return None + + def verify_nat_detection(self, packet): + if self.ip6: + iph = packet[IPv6] + else: + iph = packet[IP] + udp = packet[UDP] + + # NAT_DETECTION_SOURCE_IP + s = self.find_notify_payload(packet, 16388) + self.assertIsNotNone(s) + src_sha = self.sa.compute_nat_sha1( + inet_pton(socket.AF_INET, iph.src), udp.sport, b'\x00' * 8) + self.assertEqual(s.load, src_sha) + + # NAT_DETECTION_DESTINATION_IP + s = self.find_notify_payload(packet, 16389) + self.assertIsNotNone(s) + dst_sha = self.sa.compute_nat_sha1( + inet_pton(socket.AF_INET, iph.dst), udp.dport, b'\x00' * 8) + self.assertEqual(s.load, dst_sha) + + def verify_sa_init_request(self, packet): + udp = packet[UDP] + self.sa.dport = udp.sport + ih = packet[ikev2.IKEv2] + self.assertNotEqual(ih.init_SPI, 8 * b'\x00') + self.assertEqual(ih.exch_type, 34) # SA_INIT + self.sa.ispi = ih.init_SPI + self.assertEqual(ih.resp_SPI, 8 * b'\x00') + self.assertIn('Initiator', ih.flags) + self.assertNotIn('Response', ih.flags) + self.sa.i_nonce = ih[ikev2.IKEv2_payload_Nonce].load + self.sa.i_dh_data = ih[ikev2.IKEv2_payload_KE].load + + prop = packet[ikev2.IKEv2_payload_Proposal] + self.assertEqual(prop.proto, 1) # proto = ikev2 + self.assertEqual(prop.proposal, 1) + self.assertEqual(prop.trans[0].transform_type, 1) # encryption + self.assertEqual(prop.trans[0].transform_id, + self.p.ike_transforms['crypto_alg']) + self.assertEqual(prop.trans[1].transform_type, 2) # prf + self.assertEqual(prop.trans[1].transform_id, 5) # "hmac-sha2-256" + self.assertEqual(prop.trans[2].transform_type, 4) # dh + self.assertEqual(prop.trans[2].transform_id, + self.p.ike_transforms['dh_group']) + + self.verify_nat_detection(packet) + self.sa.set_ike_props( + crypto='AES-GCM-16ICV', crypto_key_len=32, + integ='NULL', prf='PRF_HMAC_SHA2_256', dh='3072MODPgr') + self.sa.set_esp_props(crypto='AES-CBC', crypto_key_len=32, + integ='SHA2-256-128') + self.sa.generate_dh_data() + self.sa.complete_dh_data() + self.sa.calc_keys() + + def update_esp_transforms(self, trans, sa): + while trans: + if trans.transform_type == 1: # ecryption + sa.esp_crypto = CRYPTO_IDS[trans.transform_id] + elif trans.transform_type == 3: # integrity + sa.esp_integ = INTEG_IDS[trans.transform_id] + trans = trans.payload + + def verify_sa_auth_req(self, packet): + udp = packet[UDP] + self.sa.dport = udp.sport + ih = self.get_ike_header(packet) + self.assertEqual(ih.resp_SPI, self.sa.rspi) + self.assertEqual(ih.init_SPI, self.sa.ispi) + self.assertEqual(ih.exch_type, 35) # IKE_AUTH + self.assertIn('Initiator', ih.flags) + self.assertNotIn('Response', ih.flags) + + udp = packet[UDP] + self.verify_udp(udp) + self.assertEqual(ih.id, self.sa.msg_id + 1) + self.sa.msg_id += 1 + plain = self.sa.hmac_and_decrypt(ih) + idi = ikev2.IKEv2_payload_IDi(plain) + idr = ikev2.IKEv2_payload_IDr(idi.payload) + self.assertEqual(idi.load, self.sa.i_id) + self.assertEqual(idr.load, self.sa.r_id) + prop = idi[ikev2.IKEv2_payload_Proposal] + c = self.sa.child_sas[0] + c.ispi = prop.SPI + self.update_esp_transforms( + prop[ikev2.IKEv2_payload_Transform], self.sa) + + def send_init_response(self): + tr_attr = self.sa.ike_crypto_attr() + trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption', + transform_id=self.sa.ike_crypto, length=tr_attr[1], + key_length=tr_attr[0]) / + ikev2.IKEv2_payload_Transform(transform_type='Integrity', + transform_id=self.sa.ike_integ) / + ikev2.IKEv2_payload_Transform(transform_type='PRF', + transform_id=self.sa.ike_prf_alg.name) / + ikev2.IKEv2_payload_Transform(transform_type='GroupDesc', + transform_id=self.sa.ike_dh)) + props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='IKEv2', + trans_nb=4, trans=trans)) + + src_address = inet_pton(socket.AF_INET, self.pg0.remote_ip4) + if self.sa.natt: + dst_address = b'\x0a\x0a\x0a\x0a' + else: + dst_address = inet_pton(socket.AF_INET, self.pg0.local_ip4) + src_nat = self.sa.compute_nat_sha1(src_address, self.sa.sport) + dst_nat = self.sa.compute_nat_sha1(dst_address, self.sa.dport) + + self.sa.init_resp_packet = ( + ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + exch_type='IKE_SA_INIT', flags='Response') / + ikev2.IKEv2_payload_SA(next_payload='KE', prop=props) / + ikev2.IKEv2_payload_KE(next_payload='Nonce', + group=self.sa.ike_dh, + load=self.sa.my_dh_pub_key) / + ikev2.IKEv2_payload_Nonce(load=self.sa.r_nonce, + next_payload='Notify') / + ikev2.IKEv2_payload_Notify( + type='NAT_DETECTION_SOURCE_IP', load=src_nat, + next_payload='Notify') / ikev2.IKEv2_payload_Notify( + type='NAT_DETECTION_DESTINATION_IP', load=dst_nat)) + + ike_msg = self.create_packet(self.pg0, self.sa.init_resp_packet, + self.sa.sport, self.sa.dport, + False, self.ip6) + self.pg_send(self.pg0, ike_msg) + capture = self.pg0.get_capture(1) + self.verify_sa_auth_req(capture[0]) + + def initiate_sa_init(self): + self.pg0.enable_capture() + self.pg_start() + self.vapi.ikev2_initiate_sa_init(name=self.p.profile_name) + + capture = self.pg0.get_capture(1) + self.verify_sa_init_request(capture[0]) + self.send_init_response() + + def send_auth_response(self): + tr_attr = self.sa.esp_crypto_attr() + trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption', + transform_id=self.sa.esp_crypto, length=tr_attr[1], + key_length=tr_attr[0]) / + ikev2.IKEv2_payload_Transform(transform_type='Integrity', + transform_id=self.sa.esp_integ) / + ikev2.IKEv2_payload_Transform( + transform_type='Extended Sequence Number', + transform_id='No ESN') / + ikev2.IKEv2_payload_Transform( + transform_type='Extended Sequence Number', + transform_id='ESN')) + + c = self.sa.child_sas[0] + props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP', + SPIsize=4, SPI=c.rspi, trans_nb=4, trans=trans)) + + tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4) + plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr', + IDtype=self.sa.id_type, load=self.sa.i_id) / + ikev2.IKEv2_payload_IDr(next_payload='AUTH', + IDtype=self.sa.id_type, load=self.sa.r_id) / + ikev2.IKEv2_payload_AUTH(next_payload='SA', + auth_type=AuthMethod.value(self.sa.auth_method), + load=self.sa.auth_data) / + ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) / + ikev2.IKEv2_payload_TSi(next_payload='TSr', + number_of_TSs=len(tsi), + traffic_selector=tsi) / + ikev2.IKEv2_payload_TSr(next_payload='Notify', + number_of_TSs=len(tsr), + traffic_selector=tsr) / + ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT')) + + header = ikev2.IKEv2( + init_SPI=self.sa.ispi, + resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(), + flags='Response', exch_type='IKE_AUTH') + + ike_msg = self.encrypt_ike_msg(header, plain, 'IDi') + packet = self.create_packet(self.pg0, ike_msg, self.sa.sport, + self.sa.dport, self.sa.natt, self.ip6) + self.pg_send(self.pg0, packet) + + def test_initiator(self): + self.initiate_sa_init() + self.sa.auth_init() + self.sa.calc_child_keys() + self.send_auth_response() + self.verify_ike_sas() + + +class TemplateResponder(IkePeer): + """ responder test template """ + + def initiate_del_sa_from_responder(self): + self.pg0.enable_capture() + self.pg_start() + self.vapi.ikev2_initiate_del_ike_sa( + ispi=int.from_bytes(self.sa.ispi, 'little')) + capture = self.pg0.get_capture(1) + ih = self.get_ike_header(capture[0]) + self.assertNotIn('Response', ih.flags) + self.assertNotIn('Initiator', ih.flags) + self.assertEqual(ih.exch_type, 37) # INFORMATIONAL + plain = self.sa.hmac_and_decrypt(ih) + d = ikev2.IKEv2_payload_Delete(plain) + self.assertEqual(d.proto, 1) # proto=IKEv2 + self.assertEqual(ih.init_SPI, self.sa.ispi) + self.assertEqual(ih.resp_SPI, self.sa.rspi) + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + flags='Initiator+Response', + exch_type='INFORMATIONAL', + id=ih.id, next_payload='Encrypted') + resp = self.encrypt_ike_msg(header, b'', None) + self.send_and_assert_no_replies(self.pg0, resp) + + def verify_del_sa(self, packet): + ih = self.get_ike_header(packet) + self.assertEqual(ih.id, self.sa.msg_id) + self.assertEqual(ih.exch_type, 37) # exchange informational + self.assertIn('Response', ih.flags) + self.assertNotIn('Initiator', ih.flags) + self.assertEqual(ih.next_payload, 46) # Encrypted + self.assertEqual(ih.init_SPI, self.sa.ispi) + self.assertEqual(ih.resp_SPI, self.sa.rspi) + plain = self.sa.hmac_and_decrypt(ih) + self.assertEqual(plain, b'') + + def initiate_del_sa_from_initiator(self): + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + flags='Initiator', exch_type='INFORMATIONAL', + id=self.sa.new_msg_id()) + del_sa = ikev2.IKEv2_payload_Delete(proto='IKEv2') + ike_msg = self.encrypt_ike_msg(header, del_sa, 'Delete') + packet = self.create_packet(self.pg0, ike_msg, + self.sa.sport, self.sa.dport, + self.sa.natt, self.ip6) + self.pg0.add_stream(packet) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + self.verify_del_sa(capture[0]) + + def send_sa_init_req(self): + tr_attr = self.sa.ike_crypto_attr() + trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption', + transform_id=self.sa.ike_crypto, length=tr_attr[1], + key_length=tr_attr[0]) / + ikev2.IKEv2_payload_Transform(transform_type='Integrity', + transform_id=self.sa.ike_integ) / + ikev2.IKEv2_payload_Transform(transform_type='PRF', + transform_id=self.sa.ike_prf_alg.name) / + ikev2.IKEv2_payload_Transform(transform_type='GroupDesc', + transform_id=self.sa.ike_dh)) + + props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='IKEv2', + trans_nb=4, trans=trans)) + + next_payload = None if self.ip6 else 'Notify' + + self.sa.init_req_packet = ( + ikev2.IKEv2(init_SPI=self.sa.ispi, + flags='Initiator', exch_type='IKE_SA_INIT') / + ikev2.IKEv2_payload_SA(next_payload='KE', prop=props) / + ikev2.IKEv2_payload_KE(next_payload='Nonce', + group=self.sa.ike_dh, + load=self.sa.my_dh_pub_key) / + ikev2.IKEv2_payload_Nonce(next_payload=next_payload, + load=self.sa.i_nonce)) + + if not self.ip6: + if self.sa.i_natt: + src_address = b'\x0a\x0a\x0a\x01' + else: + src_address = inet_pton(socket.AF_INET, self.pg0.remote_ip4) + + if self.sa.r_natt: + dst_address = b'\x0a\x0a\x0a\x0a' + else: + dst_address = inet_pton(socket.AF_INET, self.pg0.local_ip4) + + src_nat = self.sa.compute_nat_sha1(src_address, self.sa.sport) + dst_nat = self.sa.compute_nat_sha1(dst_address, self.sa.dport) + nat_src_detection = ikev2.IKEv2_payload_Notify( + type='NAT_DETECTION_SOURCE_IP', load=src_nat, + next_payload='Notify') + nat_dst_detection = ikev2.IKEv2_payload_Notify( + type='NAT_DETECTION_DESTINATION_IP', load=dst_nat) + self.sa.init_req_packet = (self.sa.init_req_packet / + nat_src_detection / + nat_dst_detection) + + ike_msg = self.create_packet(self.pg0, self.sa.init_req_packet, + self.sa.sport, self.sa.dport, + self.sa.natt, self.ip6) + self.pg0.add_stream(ike_msg) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + self.verify_sa_init(capture[0]) + + def generate_auth_payload(self, last_payload=None, is_rekey=False): + tr_attr = self.sa.esp_crypto_attr() + last_payload = last_payload or 'Notify' + trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption', + transform_id=self.sa.esp_crypto, length=tr_attr[1], + key_length=tr_attr[0]) / + ikev2.IKEv2_payload_Transform(transform_type='Integrity', + transform_id=self.sa.esp_integ) / + ikev2.IKEv2_payload_Transform( + transform_type='Extended Sequence Number', + transform_id='No ESN') / + ikev2.IKEv2_payload_Transform( + transform_type='Extended Sequence Number', + transform_id='ESN')) + + c = self.sa.child_sas[0] + props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP', + SPIsize=4, SPI=c.ispi, trans_nb=4, trans=trans)) + + tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4) + plain = (ikev2.IKEv2_payload_AUTH(next_payload='SA', + auth_type=AuthMethod.value(self.sa.auth_method), + load=self.sa.auth_data) / + ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) / + ikev2.IKEv2_payload_TSi(next_payload='TSr', + number_of_TSs=len(tsi), traffic_selector=tsi) / + ikev2.IKEv2_payload_TSr(next_payload=last_payload, + number_of_TSs=len(tsr), traffic_selector=tsr)) + + if is_rekey: + first_payload = 'Nonce' + plain = (ikev2.IKEv2_payload_Nonce(load=self.sa.i_nonce, + next_payload='SA') / plain / + ikev2.IKEv2_payload_Notify(type='REKEY_SA', + proto='ESP', SPI=c.ispi)) + else: + first_payload = 'IDi' + ids = (ikev2.IKEv2_payload_IDi(next_payload='IDr', + IDtype=self.sa.id_type, load=self.sa.i_id) / + ikev2.IKEv2_payload_IDr(next_payload='AUTH', + IDtype=self.sa.id_type, load=self.sa.r_id)) + plain = ids / plain + return plain, first_payload + + def send_sa_auth(self): + plain, first_payload = self.generate_auth_payload( + last_payload='Notify') + plain = plain / ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT') + header = ikev2.IKEv2( + init_SPI=self.sa.ispi, + resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(), + flags='Initiator', exch_type='IKE_AUTH') + + ike_msg = self.encrypt_ike_msg(header, plain, first_payload) + packet = self.create_packet(self.pg0, ike_msg, self.sa.sport, + self.sa.dport, self.sa.natt, self.ip6) + self.pg0.add_stream(packet) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + self.verify_sa_auth_resp(capture[0]) + + def verify_sa_init(self, packet): + ih = self.get_ike_header(packet) + + self.assertEqual(ih.id, self.sa.msg_id) + self.assertEqual(ih.exch_type, 34) + self.assertIn('Response', ih.flags) + self.assertEqual(ih.init_SPI, self.sa.ispi) + self.assertNotEqual(ih.resp_SPI, 0) + self.sa.rspi = ih.resp_SPI + try: + sa = ih[ikev2.IKEv2_payload_SA] + self.sa.r_nonce = ih[ikev2.IKEv2_payload_Nonce].load + self.sa.r_dh_data = ih[ikev2.IKEv2_payload_KE].load + except IndexError as e: + self.logger.error("unexpected reply: SA/Nonce/KE payload found!") + self.logger.error(ih.show()) + raise + self.sa.complete_dh_data() + self.sa.calc_keys() + self.sa.auth_init() + + def verify_sa_auth_resp(self, packet): + ike = self.get_ike_header(packet) + udp = packet[UDP] + self.verify_udp(udp) + self.assertEqual(ike.id, self.sa.msg_id) + plain = self.sa.hmac_and_decrypt(ike) + idr = ikev2.IKEv2_payload_IDr(plain) + prop = idr[ikev2.IKEv2_payload_Proposal] + self.assertEqual(prop.SPIsize, 4) + self.sa.child_sas[0].rspi = prop.SPI + self.sa.calc_child_keys() + def test_responder(self): - self.send_sa_init(self.sa.natt) + self.send_sa_init_req() self.send_sa_auth() self.verify_ipsec_sas() self.verify_ike_sas() @@ -838,8 +1340,16 @@ def config_params(self, params={}): 'SHA2-384-192': ei.IPSEC_API_INTEG_ALG_SHA_384_192, 'SHA2-512-256': ei.IPSEC_API_INTEG_ALG_SHA_512_256} - is_natt = 'natt' in params and params['natt'] or False + dpd_disabled = True if 'dpd_disabled' not in params else\ + params['dpd_disabled'] + if dpd_disabled: + self.vapi.cli('ikev2 dpd disable') + self.del_sa_from_responder = False if 'del_sa_from_responder'\ + not in params else params['del_sa_from_responder'] + i_natt = False if 'i_natt' not in params else params['i_natt'] + r_natt = False if 'r_natt' not in params else params['r_natt'] self.p = Profile(self, 'pr1') + self.ip6 = False if 'ip6' not in params else params['ip6'] if 'auth' in params and params['auth'] == 'rsa-sig': auth_method = 'rsa-sig' @@ -863,35 +1373,62 @@ def config_params(self, params={}): auth_method = 'shared-key' client_priv = None - self.p.add_local_id(id_type='fqdn', data=b'vpp.home') - self.p.add_remote_id(id_type='fqdn', data=b'roadwarrior.example.com') - self.p.add_local_ts(start_addr='10.10.10.0', end_addr='10.10.10.255') - self.p.add_remote_ts(start_addr='10.0.0.0', end_addr='10.0.0.255') + is_init = True if 'is_initiator' not in params else\ + params['is_initiator'] - self.sa = IKEv2SA(self, i_id=self.p.remote_id['data'], - r_id=self.p.local_id['data'], - id_type=self.p.local_id['id_type'], natt=is_natt, + idr = {'id_type': 'fqdn', 'data': b'vpp.home'} + idi = {'id_type': 'fqdn', 'data': b'roadwarrior.example.com'} + if is_init: + self.p.add_local_id(**idr) + self.p.add_remote_id(**idi) + else: + self.p.add_local_id(**idi) + self.p.add_remote_id(**idr) + + loc_ts = {'start_addr': '10.10.10.0', 'end_addr': '10.10.10.255'} if\ + 'loc_ts' not in params else params['loc_ts'] + rem_ts = {'start_addr': '10.0.0.0', 'end_addr': '10.0.0.255'} if\ + 'rem_ts' not in params else params['rem_ts'] + self.p.add_local_ts(**loc_ts) + self.p.add_remote_ts(**rem_ts) + if 'responder' in params: + self.p.add_responder(params['responder']) + if 'ike_transforms' in params: + self.p.add_ike_transforms(params['ike_transforms']) + if 'esp_transforms' in params: + self.p.add_esp_transforms(params['esp_transforms']) + + udp_encap = False if 'udp_encap' not in params else\ + params['udp_encap'] + if udp_encap: + self.p.set_udp_encap(True) + + self.sa = IKEv2SA(self, i_id=idi['data'], r_id=idr['data'], + is_initiator=is_init, + id_type=self.p.local_id['id_type'], + i_natt=i_natt, r_natt=r_natt, priv_key=client_priv, auth_method=auth_method, - auth_data=auth_data, + auth_data=auth_data, udp_encap=udp_encap, local_ts=self.p.remote_ts, remote_ts=self.p.local_ts) - - ike_crypto = ('AES-CBC', 32) if 'ike-crypto' not in params else\ - params['ike-crypto'] - ike_integ = 'HMAC-SHA1-96' if 'ike-integ' not in params else\ - params['ike-integ'] - ike_dh = '2048MODPgr' if 'ike-dh' not in params else params['ike-dh'] - - esp_crypto = ('AES-CBC', 32) if 'esp-crypto' not in params else\ - params['esp-crypto'] - esp_integ = 'HMAC-SHA1-96' if 'esp-integ' not in params else\ - params['esp-integ'] - - self.sa.set_ike_props( - crypto=ike_crypto[0], crypto_key_len=ike_crypto[1], - integ=ike_integ, prf='PRF_HMAC_SHA2_256', dh=ike_dh) - self.sa.set_esp_props( - crypto=esp_crypto[0], crypto_key_len=esp_crypto[1], - integ=esp_integ) + if is_init: + ike_crypto = ('AES-CBC', 32) if 'ike-crypto' not in params else\ + params['ike-crypto'] + ike_integ = 'HMAC-SHA1-96' if 'ike-integ' not in params else\ + params['ike-integ'] + ike_dh = '2048MODPgr' if 'ike-dh' not in params else\ + params['ike-dh'] + + esp_crypto = ('AES-CBC', 32) if 'esp-crypto' not in params else\ + params['esp-crypto'] + esp_integ = 'HMAC-SHA1-96' if 'esp-integ' not in params else\ + params['esp-integ'] + + self.sa.set_ike_props( + crypto=ike_crypto[0], crypto_key_len=ike_crypto[1], + integ=ike_integ, prf='PRF_HMAC_SHA2_256', dh=ike_dh) + self.sa.set_esp_props( + crypto=esp_crypto[0], crypto_key_len=esp_crypto[1], + integ=esp_integ) class TestApi(VppTestCase): @@ -927,19 +1464,21 @@ def configure_profile(self, cfg): p.set_lifetime_data(cfg['lifetime_data']) if 'tun_itf' in cfg: p.set_tunnel_interface(cfg['tun_itf']) + if 'natt_disabled' in cfg and cfg['natt_disabled']: + p.disable_natt() p.add_vpp_config() return p def test_profile_api(self): """ test profile dump API """ - loc_ts = { + loc_ts4 = { 'proto': 8, 'start_port': 1, 'end_port': 19, 'start_addr': '3.3.3.2', 'end_addr': '3.3.3.3', } - rem_ts = { + rem_ts4 = { 'proto': 9, 'start_port': 10, 'end_port': 119, @@ -947,14 +1486,30 @@ def test_profile_api(self): 'end_addr': '2.3.4.6', } + loc_ts6 = { + 'proto': 8, + 'start_port': 1, + 'end_port': 19, + 'start_addr': 'ab::1', + 'end_addr': 'ab::4', + } + rem_ts6 = { + 'proto': 9, + 'start_port': 10, + 'end_port': 119, + 'start_addr': 'cd::12', + 'end_addr': 'cd::13', + } + conf = { 'p1': { 'name': 'p1', + 'natt_disabled': True, 'loc_id': ('fqdn', b'vpp.home'), 'rem_id': ('fqdn', b'roadwarrior.example.com'), - 'loc_ts': loc_ts, - 'rem_ts': rem_ts, - 'responder': {'sw_if_index': 0, 'ip4': '5.6.7.8'}, + 'loc_ts': loc_ts4, + 'rem_ts': rem_ts4, + 'responder': {'sw_if_index': 0, 'addr': '5.6.7.8'}, 'ike_ts': { 'crypto_alg': 20, 'crypto_key_size': 32, @@ -976,10 +1531,10 @@ def test_profile_api(self): 'p2': { 'name': 'p2', 'loc_id': ('ip4-addr', b'192.168.2.1'), - 'rem_id': ('ip4-addr', b'192.168.2.2'), - 'loc_ts': loc_ts, - 'rem_ts': rem_ts, - 'responder': {'sw_if_index': 4, 'ip4': '5.6.7.99'}, + 'rem_id': ('ip6-addr', b'abcd::1'), + 'loc_ts': loc_ts6, + 'rem_ts': rem_ts6, + 'responder': {'sw_if_index': 4, 'addr': 'def::10'}, 'ike_ts': { 'crypto_alg': 12, 'crypto_key_size': 16, @@ -1010,12 +1565,14 @@ def verify_ts(self, api_ts, cfg_ts): self.assertEqual(api_ts.protocol_id, cfg_ts['proto']) self.assertEqual(api_ts.start_port, cfg_ts['start_port']) self.assertEqual(api_ts.end_port, cfg_ts['end_port']) - self.assertEqual(api_ts.start_addr, IPv4Address(cfg_ts['start_addr'])) - self.assertEqual(api_ts.end_addr, IPv4Address(cfg_ts['end_addr'])) + self.assertEqual(api_ts.start_addr, + ip_address(text_type(cfg_ts['start_addr']))) + self.assertEqual(api_ts.end_addr, + ip_address(text_type(cfg_ts['end_addr']))) def verify_responder(self, api_r, cfg_r): self.assertEqual(api_r.sw_if_index, cfg_r['sw_if_index']) - self.assertEqual(api_r.ip4, IPv4Address(cfg_r['ip4'])) + self.assertEqual(api_r.addr, ip_address(cfg_r['addr'])) def verify_transforms(self, api_ts, cfg_ts): self.assertEqual(api_ts.crypto_alg, cfg_ts['crypto_alg']) @@ -1051,6 +1608,9 @@ def verify_profile(self, ap, cp): self.verify_ike_transforms(ap.ike_ts, cp['ike_ts']) self.verify_esp_transforms(ap.esp_ts, cp['esp_ts']) self.verify_auth(ap.auth, cp['auth']) + natt_dis = False if 'natt_disabled' not in cp else cp['natt_disabled'] + self.assertTrue(natt_dis == ap.natt_disabled) + if 'lifetime_data' in cp: self.verify_lifetime_data(ap, cp['lifetime_data']) self.assertEqual(ap.ipsec_over_udp_port, cp['ipsec_over_udp_port']) @@ -1060,11 +1620,170 @@ def verify_profile(self, ap, cp): self.assertEqual(ap.tun_itf, 0xffffffff) -class TestResponderNATT(TemplateResponder, Ikev2Params): - """ test ikev2 responder - nat traversal """ +class TestResponderBehindNAT(TemplateResponder, Ikev2Params): + """ test responder - responder behind NAT """ + + def config_tc(self): + self.config_params({'r_natt': True}) + + +class TestInitiatorNATT(TemplateInitiator, Ikev2Params): + """ test ikev2 initiator - NAT traversal (intitiator behind NAT) """ + + def config_tc(self): + self.config_params({ + 'i_natt': True, + 'is_initiator': False, # seen from test case perspective + # thus vpp is initiator + 'responder': {'sw_if_index': self.pg0.sw_if_index, + 'addr': self.pg0.remote_ip4}, + 'ike-crypto': ('AES-GCM-16ICV', 32), + 'ike-integ': 'NULL', + 'ike-dh': '3072MODPgr', + 'ike_transforms': { + 'crypto_alg': 20, # "aes-gcm-16" + 'crypto_key_size': 256, + 'dh_group': 15, # "modp-3072" + }, + 'esp_transforms': { + 'crypto_alg': 12, # "aes-cbc" + 'crypto_key_size': 256, + # "hmac-sha2-256-128" + 'integ_alg': 12}}) + + +class TestInitiatorPsk(TemplateInitiator, Ikev2Params): + """ test ikev2 initiator - pre shared key auth """ + + def config_tc(self): + self.config_params({ + 'is_initiator': False, # seen from test case perspective + # thus vpp is initiator + 'responder': {'sw_if_index': self.pg0.sw_if_index, + 'addr': self.pg0.remote_ip4}, + 'ike-crypto': ('AES-GCM-16ICV', 32), + 'ike-integ': 'NULL', + 'ike-dh': '3072MODPgr', + 'ike_transforms': { + 'crypto_alg': 20, # "aes-gcm-16" + 'crypto_key_size': 256, + 'dh_group': 15, # "modp-3072" + }, + 'esp_transforms': { + 'crypto_alg': 12, # "aes-cbc" + 'crypto_key_size': 256, + # "hmac-sha2-256-128" + 'integ_alg': 12}}) + + +class TestInitiatorRequestWindowSize(TestInitiatorPsk): + """ test initiator - request window size (1) """ + + def rekey_respond(self, req, update_child_sa_data): + ih = self.get_ike_header(req) + plain = self.sa.hmac_and_decrypt(ih) + sa = ikev2.IKEv2_payload_SA(plain) + if update_child_sa_data: + prop = sa[ikev2.IKEv2_payload_Proposal] + self.sa.i_nonce = sa[ikev2.IKEv2_payload_Nonce].load + self.sa.r_nonce = self.sa.i_nonce + self.sa.child_sas[0].ispi = prop.SPI + self.sa.child_sas[0].rspi = prop.SPI + self.sa.calc_child_keys() + + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + flags='Response', exch_type=36, + id=ih.id, next_payload='Encrypted') + resp = self.encrypt_ike_msg(header, sa, 'SA') + packet = self.create_packet(self.pg0, resp, self.sa.sport, + self.sa.dport, self.sa.natt, self.ip6) + self.send_and_assert_no_replies(self.pg0, packet) + + def test_initiator(self): + super(TestInitiatorRequestWindowSize, self).test_initiator() + self.pg0.enable_capture() + self.pg_start() + ispi = int.from_bytes(self.sa.child_sas[0].ispi, 'little') + self.vapi.ikev2_initiate_rekey_child_sa(ispi=ispi) + self.vapi.ikev2_initiate_rekey_child_sa(ispi=ispi) + capture = self.pg0.get_capture(2) + + # reply in reverse order + self.rekey_respond(capture[1], True) + self.rekey_respond(capture[0], False) + + # verify that only the second request was accepted + self.verify_ike_sas() + self.verify_ipsec_sas(is_rekey=True) + + +class TestInitiatorRekey(TestInitiatorPsk): + """ test ikev2 initiator - rekey """ + + def rekey_from_initiator(self): + ispi = int.from_bytes(self.sa.child_sas[0].ispi, 'little') + self.pg0.enable_capture() + self.pg_start() + self.vapi.ikev2_initiate_rekey_child_sa(ispi=ispi) + capture = self.pg0.get_capture(1) + ih = self.get_ike_header(capture[0]) + self.assertEqual(ih.exch_type, 36) # CHILD_SA + self.assertNotIn('Response', ih.flags) + self.assertIn('Initiator', ih.flags) + plain = self.sa.hmac_and_decrypt(ih) + sa = ikev2.IKEv2_payload_SA(plain) + prop = sa[ikev2.IKEv2_payload_Proposal] + self.sa.i_nonce = sa[ikev2.IKEv2_payload_Nonce].load + self.sa.r_nonce = self.sa.i_nonce + # update new responder SPI + self.sa.child_sas[0].ispi = prop.SPI + self.sa.child_sas[0].rspi = prop.SPI + self.sa.calc_child_keys() + header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi, + flags='Response', exch_type=36, + id=ih.id, next_payload='Encrypted') + resp = self.encrypt_ike_msg(header, sa, 'SA') + packet = self.create_packet(self.pg0, resp, self.sa.sport, + self.sa.dport, self.sa.natt, self.ip6) + self.send_and_assert_no_replies(self.pg0, packet) + + def test_initiator(self): + super(TestInitiatorRekey, self).test_initiator() + self.rekey_from_initiator() + self.verify_ike_sas() + self.verify_ipsec_sas(is_rekey=True) + + +class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params): + """ test ikev2 initiator - delete IKE SA from responder """ + + def config_tc(self): + self.config_params({ + 'del_sa_from_responder': True, + 'is_initiator': False, # seen from test case perspective + # thus vpp is initiator + 'responder': {'sw_if_index': self.pg0.sw_if_index, + 'addr': self.pg0.remote_ip4}, + 'ike-crypto': ('AES-GCM-16ICV', 32), + 'ike-integ': 'NULL', + 'ike-dh': '3072MODPgr', + 'ike_transforms': { + 'crypto_alg': 20, # "aes-gcm-16" + 'crypto_key_size': 256, + 'dh_group': 15, # "modp-3072" + }, + 'esp_transforms': { + 'crypto_alg': 12, # "aes-cbc" + 'crypto_key_size': 256, + # "hmac-sha2-256-128" + 'integ_alg': 12}}) + + +class TestResponderInitBehindNATT(TemplateResponder, Ikev2Params): + """ test ikev2 responder - initiator behind NAT """ def config_tc(self): self.config_params( - {'natt': True}) + {'i_natt': True}) class TestResponderPsk(TemplateResponder, Ikev2Params): @@ -1073,10 +1792,65 @@ def config_tc(self): self.config_params() +class TestResponderDpd(TestResponderPsk): + """ + Dead peer detection test + """ + def config_tc(self): + self.config_params({'dpd_disabled': False}) + + def tearDown(self): + pass + + def test_responder(self): + self.vapi.ikev2_profile_set_liveness(period=2, max_retries=1) + super(TestResponderDpd, self).test_responder() + self.pg0.enable_capture() + self.pg_start() + # capture empty request but don't reply + capture = self.pg0.get_capture(expected_count=1, timeout=5) + ih = self.get_ike_header(capture[0]) + self.assertEqual(ih.exch_type, 37) # INFORMATIONAL + plain = self.sa.hmac_and_decrypt(ih) + self.assertEqual(plain, b'') + # wait for SA expiration + time.sleep(3) + ike_sas = self.vapi.ikev2_sa_dump() + self.assertEqual(len(ike_sas), 0) + ipsec_sas = self.vapi.ipsec_sa_dump() + self.assertEqual(len(ipsec_sas), 0) + + +class TestResponderRekey(TestResponderPsk): + """ test ikev2 responder - rekey """ + + def rekey_from_initiator(self): + packet = self.create_rekey_request() + self.pg0.add_stream(packet) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + ih = self.get_ike_header(capture[0]) + plain = self.sa.hmac_and_decrypt(ih) + sa = ikev2.IKEv2_payload_SA(plain) + prop = sa[ikev2.IKEv2_payload_Proposal] + self.sa.r_nonce = sa[ikev2.IKEv2_payload_Nonce].load + # update new responder SPI + self.sa.child_sas[0].rspi = prop.SPI + + def test_responder(self): + super(TestResponderRekey, self).test_responder() + self.rekey_from_initiator() + self.sa.calc_child_keys() + self.verify_ike_sas() + self.verify_ipsec_sas(is_rekey=True) + + class TestResponderRsaSign(TemplateResponder, Ikev2Params): """ test ikev2 responder - cert based auth """ def config_tc(self): self.config_params({ + 'udp_encap': True, 'auth': 'rsa-sig', 'server-key': 'server-key.pem', 'client-key': 'client-key.pem', @@ -1118,9 +1892,75 @@ class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params): """ def config_tc(self): self.config_params({ + 'del_sa_from_responder': True, + 'ip6': True, + 'natt': True, 'ike-crypto': ('AES-GCM-16ICV', 32), 'ike-integ': 'NULL', - 'ike-dh': '2048MODPgr'}) + 'ike-dh': '2048MODPgr', + 'loc_ts': {'start_addr': 'ab:cd::0', + 'end_addr': 'ab:cd::10'}, + 'rem_ts': {'start_addr': '11::0', + 'end_addr': '11::100'}}) + + +class TestInitiatorKeepaliveMsg(TestInitiatorPsk): + """ + Test for keep alive messages + """ + + def send_empty_req_from_responder(self): + packet = self.create_empty_request() + self.pg0.add_stream(packet) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + ih = self.get_ike_header(capture[0]) + self.assertEqual(ih.id, self.sa.msg_id) + plain = self.sa.hmac_and_decrypt(ih) + self.assertEqual(plain, b'') + + def test_initiator(self): + super(TestInitiatorKeepaliveMsg, self).test_initiator() + self.send_empty_req_from_responder() + + +class TestMalformedMessages(TemplateResponder, Ikev2Params): + """ malformed packet test """ + + def tearDown(self): + pass + + def config_tc(self): + self.config_params() + + def assert_counter(self, count, name, version='ip4'): + node_name = '/err/ikev2-%s/' % version + name + self.assertEqual(count, self.statistics.get_err_counter(node_name)) + + def create_ike_init_msg(self, length=None, payload=None): + msg = ikev2.IKEv2(length=length, init_SPI='\x11' * 8, + flags='Initiator', exch_type='IKE_SA_INIT') + if payload is not None: + msg /= payload + return self.create_packet(self.pg0, msg, self.sa.sport, + self.sa.dport) + + def verify_bad_packet_length(self): + ike_msg = self.create_ike_init_msg(length=0xdead) + self.send_and_assert_no_replies(self.pg0, ike_msg * self.pkt_count) + self.assert_counter(self.pkt_count, 'Bad packet length') + + def verify_bad_sa_payload_length(self): + p = ikev2.IKEv2_payload_SA(length=0xdead) + ike_msg = self.create_ike_init_msg(payload=p) + self.send_and_assert_no_replies(self.pg0, ike_msg * self.pkt_count) + self.assert_counter(self.pkt_count, 'Malformed packet') + + def test_responder(self): + self.pkt_count = 254 + self.verify_bad_packet_length() + self.verify_bad_sa_payload_length() if __name__ == '__main__': diff --git a/src/plugins/ikev2/test/vpp_ikev2.py b/src/plugins/ikev2/test/vpp_ikev2.py index 5a2a51eb6074..dd1c3fc986e6 100644 --- a/src/plugins/ikev2/test/vpp_ikev2.py +++ b/src/plugins/ikev2/test/vpp_ikev2.py @@ -1,3 +1,4 @@ +from ipaddress import IPv4Address, AddressValueError from vpp_object import VppObject from vpp_papi import VppEnum @@ -12,7 +13,8 @@ def value(key): return AuthMethod.v[key] class IDType: v = {'ip4-addr': 1, - 'fqdn': 2} + 'fqdn': 2, + 'ip6-addr': 5} @staticmethod def value(key): return IDType.v[key] @@ -25,6 +27,10 @@ def __init__(self, test, profile_name): self.vapi = test.vapi self.profile_name = profile_name self.udp_encap = False + self.natt = True + + def disable_natt(self): + self.natt = False def add_auth(self, method, data, is_hex=False): if isinstance(method, int): @@ -52,7 +58,8 @@ def add_remote_id(self, id_type, data): 'is_local': False} def add_local_ts(self, start_addr, end_addr, start_port=0, end_port=0xffff, - proto=0): + proto=0, is_ip4=True): + self.ts_is_ip4 = is_ip4 self.local_ts = {'is_local': True, 'protocol_id': proto, 'start_port': start_port, @@ -62,6 +69,12 @@ def add_local_ts(self, start_addr, end_addr, start_port=0, end_port=0xffff, def add_remote_ts(self, start_addr, end_addr, start_port=0, end_port=0xffff, proto=0): + try: + IPv4Address(start_addr) + is_ip4 = True + except AddressValueError: + is_ip4 = False + self.ts_is_ip4 = is_ip4 self.remote_ts = {'is_local': False, 'protocol_id': proto, 'start_port': start_port, @@ -115,19 +128,19 @@ def add_vpp_config(self): **self.remote_id) if hasattr(self, 'local_ts'): self.vapi.ikev2_profile_set_ts(name=self.profile_name, - ts={**self.local_ts}) + ts=self.local_ts) if hasattr(self, 'remote_ts'): self.vapi.ikev2_profile_set_ts(name=self.profile_name, - ts={**self.remote_ts}) + ts=self.remote_ts) if hasattr(self, 'responder'): self.vapi.ikev2_set_responder(name=self.profile_name, - responder={**self.responder}) + responder=self.responder) if hasattr(self, 'ike_transforms'): self.vapi.ikev2_set_ike_transforms(name=self.profile_name, - tr={**self.ike_transforms}) + tr=self.ike_transforms) if hasattr(self, 'esp_transforms'): self.vapi.ikev2_set_esp_transforms(name=self.profile_name, @@ -147,6 +160,9 @@ def add_vpp_config(self): self.vapi.ikev2_set_tunnel_interface(name=self.profile_name, sw_if_index=self.tun_itf) + if not self.natt: + self.vapi.ikev2_profile_disable_natt(name=self.profile_name) + def query_vpp_config(self): res = self.vapi.ikev2_profile_dump() for r in res: diff --git a/src/plugins/map/map.api b/src/plugins/map/map.api index adf4d0a5bbd6..fa32978591c4 100644 --- a/src/plugins/map/map.api +++ b/src/plugins/map/map.api @@ -112,7 +112,7 @@ define map_domains_get_reply define map_domain_dump { - option deprecated="v21.01"; + option deprecated; u32 client_index; u32 context; }; diff --git a/src/plugins/nat/det44/det44.api b/src/plugins/nat/det44/det44.api index 818d46a1d5ad..f9c38007288d 100644 --- a/src/plugins/nat/det44/det44.api +++ b/src/plugins/nat/det44/det44.api @@ -311,7 +311,7 @@ autoreply define nat_det_add_del_map { u8 in_plen; vl_api_ip4_address_t out_addr; u8 out_plen; - option status="deprecated"; + option deprecated; }; /** \brief Get outside address and port range from inside address @@ -323,7 +323,7 @@ define nat_det_forward { u32 client_index; u32 context; vl_api_ip4_address_t in_addr; - option status="deprecated"; + option deprecated; }; /** \brief Get outside address and port range from inside address @@ -339,7 +339,7 @@ define nat_det_forward_reply { u16 out_port_lo; u16 out_port_hi; vl_api_ip4_address_t out_addr; - option status="deprecated"; + option deprecated; }; /** \brief Get inside address from outside address and port @@ -353,7 +353,7 @@ define nat_det_reverse { u32 context; u16 out_port; vl_api_ip4_address_t out_addr; - option status="deprecated"; + option deprecated; }; /** \brief Get inside address from outside address and port reply @@ -365,7 +365,7 @@ define nat_det_reverse_reply { u32 context; i32 retval; vl_api_ip4_address_t in_addr; - option status="deprecated"; + option deprecated; }; /** \brief Dump NAT deterministic mappings @@ -375,7 +375,7 @@ define nat_det_reverse_reply { define nat_det_map_dump { u32 client_index; u32 context; - option status="deprecated"; + option deprecated; }; /** \brief NAT users response @@ -397,7 +397,7 @@ define nat_det_map_details { u32 sharing_ratio; u16 ports_per_host; u32 ses_num; - option status="deprecated"; + option deprecated; }; /** \brief Close deterministic NAT session by outside address and port @@ -415,7 +415,7 @@ autoreply define nat_det_close_session_out { u16 out_port; vl_api_ip4_address_t ext_addr; u16 ext_port; - option status="deprecated"; + option deprecated; }; /** \brief Close deterministic NAT session by inside address and port @@ -433,7 +433,7 @@ autoreply define nat_det_close_session_in { u16 in_port; vl_api_ip4_address_t ext_addr; u16 ext_port; - option status="deprecated"; + option deprecated; }; /** \brief Dump determinstic NAT sessions @@ -445,7 +445,7 @@ define nat_det_session_dump { u32 client_index; u32 context; vl_api_ip4_address_t user_addr; - option status="deprecated"; + option deprecated; }; /** \brief Deterministic NAT sessions reply @@ -465,5 +465,5 @@ define nat_det_session_details { u16 out_port; u8 state; u32 expire; - option status="deprecated"; + option deprecated; }; diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c index 448e967b8ae3..d43caef46d93 100644 --- a/src/plugins/nat/in2out_ed.c +++ b/src/plugins/nat/in2out_ed.c @@ -964,7 +964,6 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm, if (is_output_feature) { - vnet_feature_next (&vnet_buffer2 (b0)->nat.arc_next, b0); iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length; } @@ -1586,6 +1585,13 @@ VLIB_NODE_FN (nat_pre_in2out_node) NAT_NEXT_IN2OUT_ED_FAST_PATH); } +VLIB_NODE_FN (nat_pre_in2out_output_node) + (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return nat_pre_node_fn_inline (vm, node, frame, + NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH); +} + /* *INDENT-OFF* */ VLIB_REGISTER_NODE (nat_pre_in2out_node) = { .name = "nat-pre-in2out", @@ -1595,6 +1601,15 @@ VLIB_REGISTER_NODE (nat_pre_in2out_node) = { .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = 0, }; + +VLIB_REGISTER_NODE (nat_pre_in2out_output_node) = { + .name = "nat-pre-in2out-output", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_pre_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = 0, +}; /* *INDENT-ON* */ /* diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 61a36ec4e906..15c767c82c3e 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -142,6 +142,12 @@ VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { .node_name = "nat44-hairpin-src", .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), }; +VNET_FEATURE_INIT (nat_pre_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat-pre-in2out-output", + .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), + .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), +}; VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = { .arc_name = "ip4-output", .node_name = "nat44-ed-in2out-output", @@ -2249,7 +2255,7 @@ snat_interface_add_del_output_feature (u32 sw_if_index, return rv; vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in", sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-ed-in2out-output", + vnet_feature_enable_disable ("ip4-output", "nat-pre-in2out-output", sw_if_index, !is_del, 0, 0); } else @@ -4662,6 +4668,7 @@ VLIB_REGISTER_NODE (nat_default_node) = { [NAT_NEXT_ICMP_ERROR] = "ip4-icmp-error", [NAT_NEXT_IN2OUT_ED_FAST_PATH] = "nat44-ed-in2out", [NAT_NEXT_IN2OUT_ED_SLOW_PATH] = "nat44-ed-in2out-slowpath", + [NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH] = "nat44-ed-in2out-output", [NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath", [NAT_NEXT_OUT2IN_ED_FAST_PATH] = "nat44-ed-out2in", [NAT_NEXT_OUT2IN_ED_SLOW_PATH] = "nat44-ed-out2in-slowpath", diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index ab699221e698..fc5d32059ed2 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -54,6 +54,7 @@ typedef enum NAT_NEXT_ICMP_ERROR, NAT_NEXT_IN2OUT_ED_FAST_PATH, NAT_NEXT_IN2OUT_ED_SLOW_PATH, + NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH, NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH, NAT_NEXT_OUT2IN_ED_FAST_PATH, NAT_NEXT_OUT2IN_ED_SLOW_PATH, diff --git a/src/plugins/nat/nat44_hairpinning.c b/src/plugins/nat/nat44_hairpinning.c index 9eadcf308325..2859046ae05c 100644 --- a/src/plugins/nat/nat44_hairpinning.c +++ b/src/plugins/nat/nat44_hairpinning.c @@ -108,6 +108,7 @@ snat_hairpinning (vlib_main_t * vm, vlib_node_runtime_t * node, ip4_address_t sm0_addr; u16 sm0_port; u32 sm0_fib_index; + u32 old_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; /* Check if destination is static mappings */ if (!snat_static_mapping_match (sm, ip0->dst_address, udp0->dst_port, sm->outside_fib_index, proto0, @@ -159,6 +160,17 @@ snat_hairpinning (vlib_main_t * vm, vlib_node_runtime_t * node, vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; } + /* Check if anything has changed and if not, then return 0. This + helps avoid infinite loop, repeating the three nodes + nat44-hairpinning-->ip4-lookup-->ip4-local, in case nothing has + changed. */ + old_dst_addr0 = ip0->dst_address.as_u32; + old_dst_port0 = tcp0->dst; + if (new_dst_addr0 == old_dst_addr0 + && new_dst_port0 == old_dst_port0 + && vnet_buffer (b0)->sw_if_index[VLIB_TX] == old_sw_if_index) + return 0; + /* Destination is behind the same NAT, use internal address and port */ if (new_dst_addr0) { diff --git a/src/plugins/nat/nat_format.c b/src/plugins/nat/nat_format.c index 3dfe6046969f..f0e7a5d5cd00 100644 --- a/src/plugins/nat/nat_format.c +++ b/src/plugins/nat/nat_format.c @@ -230,7 +230,8 @@ format_snat_static_mapping (u8 * s, va_list * args) else s = format (s, "identity mapping %U %U:%d", format_nat_protocol, m->proto, - format_ip4_address, &m->local_addr, m->local_port); + format_ip4_address, &m->local_addr, + clib_net_to_host_u16 (m->local_port)); /* *INDENT-OFF* */ pool_foreach (local, m->locals, @@ -256,7 +257,8 @@ format_snat_static_mapping (u8 * s, va_list * args) { s = format (s, "%U external %U:%d %s %s", format_nat_protocol, m->proto, - format_ip4_address, &m->external_addr, m->external_port, + format_ip4_address, &m->external_addr, + clib_net_to_host_u16 (m->external_port), m->twice_nat == TWICE_NAT ? "twice-nat" : m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", is_out2in_only_static_mapping (m) ? "out2in-only" : ""); @@ -265,7 +267,8 @@ format_snat_static_mapping (u8 * s, va_list * args) pool_foreach (local, m->locals, ({ s = format (s, "\n local %U:%d vrf %d probability %d\%", - format_ip4_address, &local->addr, local->port, + format_ip4_address, &local->addr, + clib_net_to_host_u16 (local->port), local->vrf_id, local->probability); })); /* *INDENT-ON* */ @@ -274,8 +277,10 @@ format_snat_static_mapping (u8 * s, va_list * args) else s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s", format_nat_protocol, m->proto, - format_ip4_address, &m->local_addr, m->local_port, - format_ip4_address, &m->external_addr, m->external_port, + format_ip4_address, &m->local_addr, + clib_net_to_host_u16 (m->local_port), + format_ip4_address, &m->external_addr, + clib_net_to_host_u16 (m->external_port), m->vrf_id, m->twice_nat == TWICE_NAT ? "twice-nat" : m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", @@ -297,9 +302,10 @@ format_snat_static_map_to_resolve (u8 * s, va_list * args) else s = format (s, "%U local %U:%d external %U:%d vrf %d", format_nat_protocol, m->proto, - format_ip4_address, &m->l_addr, m->l_port, + format_ip4_address, &m->l_addr, + clib_net_to_host_u16 (m->l_port), format_vnet_sw_if_index_name, vnm, m->sw_if_index, - m->e_port, m->vrf_id); + clib_net_to_host_u16 (m->e_port), m->vrf_id); return s; } diff --git a/src/plugins/nat/test/test_nat.py b/src/plugins/nat/test/test_nat.py index a305b7aa9f48..947182811932 100644 --- a/src/plugins/nat/test/test_nat.py +++ b/src/plugins/nat/test/test_nat.py @@ -2610,6 +2610,94 @@ def test_hairpinning2(self): self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise + def test_hairpinning_avoid_inf_loop(self): + """ NAT44 hairpinning - 1:1 NAPT avoid infinite loop """ + + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + host_in_port = 1234 + host_out_port = 0 + server_in_port = 5678 + server_out_port = 8765 + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, self.nat_addr, + server_in_port, server_out_port, + proto=IP_PROTOS.tcp) + + # add another static mapping that maps pg0.local_ip4 address to itself + self.nat44_add_static_mapping(self.pg0.local_ip4, self.pg0.local_ip4) + + # send packet from host to VPP (the packet should get dropped) + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=self.pg0.local_ip4) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # Here VPP used to crash due to an infinite loop + + cnt = self.statistics.get_counter('/nat44/hairpinning')[0] + # send packet from host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=self.nat_addr) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, server.ip4) + self.assertNotEqual(tcp.sport, host_in_port) + self.assertEqual(tcp.dport, server_in_port) + self.assert_packet_checksums_valid(p) + host_out_port = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + after = self.statistics.get_counter('/nat44/hairpinning')[0] + if_idx = self.pg0.sw_if_index + self.assertEqual(after[if_idx] - cnt[if_idx], 1) + + # send reply from server to host + p = (Ether(src=server.mac, dst=self.pg0.local_mac) / + IP(src=server.ip4, dst=self.nat_addr) / + TCP(sport=server_in_port, dport=host_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, host.ip4) + self.assertEqual(tcp.sport, server_out_port) + self.assertEqual(tcp.dport, host_in_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + after = self.statistics.get_counter('/nat44/hairpinning')[0] + if_idx = self.pg0.sw_if_index + self.assertEqual(after[if_idx] - cnt[if_idx], 2) + def test_interface_addr(self): """ Acquire NAT44 addresses from interface """ self.vapi.nat44_add_del_interface_addr( diff --git a/src/plugins/nsim/nsim.api b/src/plugins/nsim/nsim.api index 654932520228..f0ae02a70bda 100644 --- a/src/plugins/nsim/nsim.api +++ b/src/plugins/nsim/nsim.api @@ -61,7 +61,7 @@ autoreply define nsim_output_feature_enable_disable */ autoreply define nsim_configure { - option deprecated="v21.01"; + option deprecated; /* Client identifier, set from api_main.my_client_index */ u32 client_index; diff --git a/src/plugins/quic/quic_crypto.c b/src/plugins/quic/quic_crypto.c index d8fd4a916e61..35dddf63c711 100644 --- a/src/plugins/quic/quic_crypto.c +++ b/src/plugins/quic/quic_crypto.c @@ -161,20 +161,19 @@ quic_crypto_setup_cipher (quicly_crypto_engine_t * engine, int ret; *aead_ctx = NULL; - /* generate new header protection key */ if (hp_ctx != NULL) { *hp_ctx = NULL; - if ((ret = - ptls_hkdf_expand_label (hash, hpkey, aead->ctr_cipher->key_size, - ptls_iovec_init (secret, - hash->digest_size), - "quic hp", ptls_iovec_init (NULL, 0), - NULL)) != 0) + ret = ptls_hkdf_expand_label (hash, hpkey, aead->ctr_cipher->key_size, + ptls_iovec_init (secret, + hash->digest_size), + "quic hp", ptls_iovec_init (NULL, 0), + NULL); + if (ret) goto Exit; - if ((*hp_ctx = - ptls_cipher_new (aead->ctr_cipher, is_enc, hpkey)) == NULL) + *hp_ctx = ptls_cipher_new (aead->ctr_cipher, is_enc, hpkey); + if (NULL == *hp_ctx) { ret = PTLS_ERROR_NO_MEMORY; goto Exit; @@ -182,9 +181,9 @@ quic_crypto_setup_cipher (quicly_crypto_engine_t * engine, } /* generate new AEAD context */ - if ((*aead_ctx = - ptls_aead_new (aead, hash, is_enc, secret, - QUICLY_AEAD_BASE_LABEL)) == NULL) + *aead_ctx = ptls_aead_new (aead, hash, is_enc, secret, + QUICLY_AEAD_BASE_LABEL); + if (NULL == *aead_ctx) { ret = PTLS_ERROR_NO_MEMORY; goto Exit; @@ -194,9 +193,7 @@ quic_crypto_setup_cipher (quicly_crypto_engine_t * engine, { quic_ctx_t *qctx = quic_get_conn_ctx (conn); if (qctx->ingress_keys.aead_ctx != NULL) - { - qctx->key_phase_ingress++; - } + qctx->key_phase_ingress++; qctx->ingress_keys.aead_ctx = *aead_ctx; if (hp_ctx != NULL) @@ -206,9 +203,9 @@ quic_crypto_setup_cipher (quicly_crypto_engine_t * engine, ret = 0; Exit: - if (ret != 0) + if (ret) { - if (aead_ctx && *aead_ctx != NULL) + if (*aead_ctx != NULL) { ptls_aead_free (*aead_ctx); *aead_ctx = NULL; diff --git a/src/plugins/rdma/device.c b/src/plugins/rdma/device.c index c91567445bf5..c2eb00062170 100644 --- a/src/plugins/rdma/device.c +++ b/src/plugins/rdma/device.c @@ -57,7 +57,7 @@ rdma_main_t rdma_main; static struct ibv_flow * rdma_rxq_init_flow (const rdma_device_t * rd, struct ibv_qp *qp, const mac_address_t * mac, const mac_address_t * mask, - u32 flags) + u16 ether_type, u32 flags) { struct ibv_flow *flow; struct raw_eth_flow_attr @@ -76,6 +76,12 @@ rdma_rxq_init_flow (const rdma_device_t * rd, struct ibv_qp *qp, memcpy (fa.spec_eth.val.dst_mac, mac, sizeof (fa.spec_eth.val.dst_mac)); memcpy (fa.spec_eth.mask.dst_mac, mask, sizeof (fa.spec_eth.mask.dst_mac)); + if (ether_type) + { + fa.spec_eth.val.ether_type = ether_type; + fa.spec_eth.mask.ether_type = 0xffff; + } + flow = ibv_create_flow (qp, &fa.attr); if (!flow) rdma_log (VLIB_LOG_LEVEL_ERR, rd, "ibv_create_flow() failed"); @@ -104,16 +110,17 @@ rdma_dev_set_promisc (rdma_device_t * rd) const mac_address_t all = {.bytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; int err; - err = rdma_rxq_destroy_flow (rd, &rd->flow_mcast); - if (err) - return ~0; - - err = rdma_rxq_destroy_flow (rd, &rd->flow_ucast); + err = rdma_rxq_destroy_flow (rd, &rd->flow_mcast6); + err |= rdma_rxq_destroy_flow (rd, &rd->flow_ucast6); + err |= rdma_rxq_destroy_flow (rd, &rd->flow_mcast4); + err |= rdma_rxq_destroy_flow (rd, &rd->flow_ucast4); if (err) return ~0; - rd->flow_ucast = rdma_rxq_init_flow (rd, rd->rx_qp, &all, &all, 0); - if (!rd->flow_ucast) + rd->flow_ucast6 = + rdma_rxq_init_flow (rd, rd->rx_qp6, &all, &all, ntohs (ETH_P_IPV6), 0); + rd->flow_ucast4 = rdma_rxq_init_flow (rd, rd->rx_qp4, &all, &all, 0, 0); + if (!rd->flow_ucast6 || !rd->flow_ucast4) return ~0; rd->flags |= RDMA_DEVICE_F_PROMISC; @@ -128,25 +135,30 @@ rdma_dev_set_ucast (rdma_device_t * rd) const mac_address_t mcast = {.bytes = {0x1, 0x0, 0x0, 0x0, 0x0, 0x0} }; int err; - err = rdma_rxq_destroy_flow (rd, &rd->flow_mcast); - if (err) - return ~0; - - err = rdma_rxq_destroy_flow (rd, &rd->flow_ucast); + err = rdma_rxq_destroy_flow (rd, &rd->flow_mcast6); + err |= rdma_rxq_destroy_flow (rd, &rd->flow_ucast6); + err |= rdma_rxq_destroy_flow (rd, &rd->flow_mcast4); + err |= rdma_rxq_destroy_flow (rd, &rd->flow_ucast4); if (err) return ~0; - /* receive only packets with src = our MAC */ - rd->flow_ucast = rdma_rxq_init_flow (rd, rd->rx_qp, &rd->hwaddr, &ucast, 0); - if (!rd->flow_ucast) - return ~0; - - /* receive multicast packets */ - rd->flow_mcast = rdma_rxq_init_flow (rd, rd->rx_qp, &mcast, &mcast, - IBV_FLOW_ATTR_FLAGS_DONT_TRAP - /* let others receive mcast packet too (eg. Linux) */ + rd->flow_ucast6 = + rdma_rxq_init_flow (rd, rd->rx_qp6, &rd->hwaddr, &ucast, + ntohs (ETH_P_IPV6), 0); + rd->flow_mcast6 = + rdma_rxq_init_flow (rd, rd->rx_qp6, &mcast, &mcast, ntohs (ETH_P_IPV6), + IBV_FLOW_ATTR_FLAGS_DONT_TRAP + /* let others receive mcast packet too (eg. Linux) */ + ); + rd->flow_ucast4 = + rdma_rxq_init_flow (rd, rd->rx_qp4, &rd->hwaddr, &ucast, 0, 0); + rd->flow_mcast4 = + rdma_rxq_init_flow (rd, rd->rx_qp4, &mcast, &mcast, 0, + IBV_FLOW_ATTR_FLAGS_DONT_TRAP + /* let others receive mcast packet too (eg. Linux) */ ); - if (!rd->flow_mcast) + if (!rd->flow_ucast6 || !rd->flow_mcast6 || !rd->flow_ucast4 + || !rd->flow_mcast4) return ~0; rd->flags &= ~RDMA_DEVICE_F_PROMISC; @@ -375,8 +387,10 @@ rdma_dev_cleanup (rdma_device_t * rd) rdma_log (VLIB_LOG_LEVEL_DEBUG, rd, #fn "() failed (rv = %d)", rv); \ } - _(ibv_destroy_flow, rd->flow_mcast); - _(ibv_destroy_flow, rd->flow_ucast); + _(ibv_destroy_flow, rd->flow_mcast6); + _(ibv_destroy_flow, rd->flow_ucast6); + _(ibv_destroy_flow, rd->flow_mcast4); + _(ibv_destroy_flow, rd->flow_ucast4); _(ibv_dereg_mr, rd->mr); vec_foreach (txq, rd->txqs) { @@ -389,7 +403,8 @@ rdma_dev_cleanup (rdma_device_t * rd) _(ibv_destroy_cq, rxq->cq); } _(ibv_destroy_rwq_ind_table, rd->rx_rwq_ind_tbl); - _(ibv_destroy_qp, rd->rx_qp); + _(ibv_destroy_qp, rd->rx_qp6); + _(ibv_destroy_qp, rd->rx_qp4); _(ibv_dealloc_pd, rd->pd); _(ibv_close_device, rd->ctx); #undef _ @@ -523,10 +538,18 @@ rdma_rxq_finalize (vlib_main_t * vm, rdma_device_t * rd) qpia.rx_hash_conf.rx_hash_key_len = sizeof (rdma_rss_hash_key); qpia.rx_hash_conf.rx_hash_key = rdma_rss_hash_key; qpia.rx_hash_conf.rx_hash_function = IBV_RX_HASH_FUNC_TOEPLITZ; + qpia.rx_hash_conf.rx_hash_fields_mask = - IBV_RX_HASH_SRC_IPV4 | IBV_RX_HASH_DST_IPV4; - if ((rd->rx_qp = ibv_create_qp_ex (rd->ctx, &qpia)) == 0) - return clib_error_return_unix (0, "Queue Pair create failed"); + IBV_RX_HASH_SRC_IPV4 | IBV_RX_HASH_DST_IPV4 | IBV_RX_HASH_SRC_PORT_TCP | + IBV_RX_HASH_DST_PORT_TCP; + if ((rd->rx_qp4 = ibv_create_qp_ex (rd->ctx, &qpia)) == 0) + return clib_error_return_unix (0, "IPv4 Queue Pair create failed"); + + qpia.rx_hash_conf.rx_hash_fields_mask = + IBV_RX_HASH_SRC_IPV6 | IBV_RX_HASH_DST_IPV6 | IBV_RX_HASH_SRC_PORT_TCP | + IBV_RX_HASH_DST_PORT_TCP; + if ((rd->rx_qp6 = ibv_create_qp_ex (rd->ctx, &qpia)) == 0) + return clib_error_return_unix (0, "IPv6 Queue Pair create failed"); if (rdma_dev_set_ucast (rd)) return clib_error_return_unix (0, "Set unicast mode failed"); diff --git a/src/plugins/rdma/output.c b/src/plugins/rdma/output.c index 5b181485e49b..8c267a6fb252 100644 --- a/src/plugins/rdma/output.c +++ b/src/plugins/rdma/output.c @@ -100,7 +100,7 @@ rdma_device_output_tx_mlx5_doorbell (rdma_txq_t * txq, rdma_mlx5_wqe_t * last, CLIB_MEMORY_STORE_BARRIER (); txq->dv_sq_dbrec[MLX5_SND_DBR] = htobe32 (tail); CLIB_COMPILER_BARRIER (); - txq->dv_sq_db[0] = *(u64 *) (txq->dv_sq_wqes + (txq->tail & sq_mask)); + txq->dv_sq_db[0] = *(u64 *) last; } static_always_inline void @@ -155,10 +155,11 @@ rdma_device_output_tx_mlx5_chained (vlib_main_t * vm, u32 sq_mask = pow2_mask (txq->dv_sq_log2sz); u32 mask = pow2_mask (txq->bufs_log2sz); u32 dseg_mask = RDMA_TXQ_DV_DSEG_SZ (txq) - 1; - const u32 lkey = wqe[0].dseg.lkey; + const u32 lkey = clib_host_to_net_u32 (rd->lkey); - vlib_buffer_copy_indices (txq->bufs + (txq->tail & mask), bi, - n_left_from - n); + vlib_buffer_copy_indices_to_ring (txq->bufs, bi, txq->tail & mask, + RDMA_TXQ_BUF_SZ (txq), n_left_from - n); + bi += n_left_from - n; while (n >= 1 && wqe_n >= 1) { @@ -264,10 +265,10 @@ rdma_device_output_tx_mlx5_chained (vlib_main_t * vm, n -= 1; } - if (n == n_left_from) - return 0; /* we fail to enqueue even a single packet */ + if (n != n_left_from) + rdma_device_output_tx_mlx5_doorbell (txq, last, tail, sq_mask); - rdma_device_output_tx_mlx5_doorbell (txq, last, tail, sq_mask); + txq->tail = tail; return n_left_from - n; } @@ -343,6 +344,9 @@ rdma_device_output_tx_mlx5 (vlib_main_t * vm, } rdma_device_output_tx_mlx5_doorbell (txq, &wqe[-1], tail, sq_mask); + vlib_buffer_copy_indices_to_ring (txq->bufs, bi, txq->tail & mask, + RDMA_TXQ_BUF_SZ (txq), n_left_from); + txq->tail = tail; return n_left_from; } @@ -389,6 +393,7 @@ rdma_device_output_tx_ibverb (vlib_main_t * vm, const rdma_device_t * rd, rdma_txq_t * txq, u32 n_left_from, u32 * bi, vlib_buffer_t ** b) { + const u32 mask = pow2_mask (txq->bufs_log2sz); struct ibv_send_wr wr[VLIB_FRAME_SIZE], *w = wr; struct ibv_sge sge[VLIB_FRAME_SIZE], *s = sge; u32 n = n_left_from; @@ -474,7 +479,9 @@ rdma_device_output_tx_ibverb (vlib_main_t * vm, n_left_from - (w - wr)); n_left_from = w - wr; } - + vlib_buffer_copy_indices_to_ring (txq->bufs, bi, txq->tail & mask, + RDMA_TXQ_BUF_SZ (txq), n_left_from); + txq->tail += n_left_from; return n_left_from; } @@ -498,7 +505,6 @@ rdma_device_output_tx_try (vlib_main_t * vm, const vlib_node_runtime_t * node, u32 n_left_from, u32 * bi, int is_mlx5dv) { vlib_buffer_t *b[VLIB_FRAME_SIZE]; - const u32 mask = pow2_mask (txq->bufs_log2sz); /* do not enqueue more packet than ring space */ n_left_from = clib_min (n_left_from, RDMA_TXQ_AVAIL_SZ (txq, txq->head, @@ -510,12 +516,11 @@ rdma_device_output_tx_try (vlib_main_t * vm, const vlib_node_runtime_t * node, vlib_get_buffers (vm, bi, b, n_left_from); n_left_from = is_mlx5dv ? - rdma_device_output_tx_mlx5 (vm, node, rd, txq, n_left_from, bi, b) : - rdma_device_output_tx_ibverb (vm, node, rd, txq, n_left_from, bi, b); - - vlib_buffer_copy_indices_to_ring (txq->bufs, bi, txq->tail & mask, - RDMA_TXQ_BUF_SZ (txq), n_left_from); - txq->tail += n_left_from; + rdma_device_output_tx_mlx5 (vm, node, rd, txq, n_left_from, bi, + b) : rdma_device_output_tx_ibverb (vm, node, + rd, txq, + n_left_from, + bi, b); return n_left_from; } diff --git a/src/plugins/rdma/rdma.h b/src/plugins/rdma/rdma.h index 016956ee0f20..19bfb8b11e56 100644 --- a/src/plugins/rdma/rdma.h +++ b/src/plugins/rdma/rdma.h @@ -172,10 +172,13 @@ typedef struct struct ibv_context *ctx; struct ibv_pd *pd; struct ibv_mr *mr; - struct ibv_qp *rx_qp; + struct ibv_qp *rx_qp4; + struct ibv_qp *rx_qp6; struct ibv_rwq_ind_table *rx_rwq_ind_tbl; - struct ibv_flow *flow_ucast; - struct ibv_flow *flow_mcast; + struct ibv_flow *flow_ucast4; + struct ibv_flow *flow_mcast4; + struct ibv_flow *flow_ucast6; + struct ibv_flow *flow_mcast6; clib_error_t *error; } rdma_device_t; diff --git a/src/plugins/unittest/svm_fifo_test.c b/src/plugins/unittest/svm_fifo_test.c index f5d4818bfe0b..a3f88d35b9cd 100644 --- a/src/plugins/unittest/svm_fifo_test.c +++ b/src/plugins/unittest/svm_fifo_test.c @@ -190,11 +190,13 @@ static svm_fifo_t * fifo_prepare (fifo_segment_t * fs, u32 fifo_size) { svm_fifo_t *f; + svm_fifo_chunk_t *c; f = fifo_segment_alloc_fifo (fs, fifo_size, FIFO_SEGMENT_RX_FIFO); - /* Paint fifo data vector with -1's */ - clib_memset (svm_fifo_head_chunk (f)->data, 0xFF, fifo_size); + /* Paint 1st fifo chunk with -1's */ + c = svm_fifo_head_chunk (f); + clib_memset (c->data, 0xFF, c->length); svm_fifo_init_ooo_lookup (f, 1 /* deq ooo */ ); return f; @@ -2726,8 +2728,8 @@ svm_fifo_test (vlib_main_t * vm, unformat_input_t * input, int res = 0; char *str; - clib_warning ("high mem %lu", HIGH_SEGMENT_BASEVA << 1); - fifo_segment_main_init (&segment_main, HIGH_SEGMENT_BASEVA << 1, 5); + clib_warning ("high mem %lu", HIGH_SEGMENT_BASEVA); + fifo_segment_main_init (&segment_main, HIGH_SEGMENT_BASEVA, 5); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "fifo1")) diff --git a/src/plugins/vmxnet3/FEATURE.yaml b/src/plugins/vmxnet3/FEATURE.yaml index 1e303f6d6ee6..d204659fb34e 100644 --- a/src/plugins/vmxnet3/FEATURE.yaml +++ b/src/plugins/vmxnet3/FEATURE.yaml @@ -4,6 +4,7 @@ maintainer: Steven Luong features: - vmxnet3 device driver to connect to ESXi server, VMWare Fusion, and VMWare Workstation + - Supports GSO. It was tested on ESXi 6.7 description: "vmxnet3 device driver support" state: production properties: [API, CLI, STATS, MULTITHREAD] diff --git a/src/plugins/vmxnet3/input.c b/src/plugins/vmxnet3/input.c index 173ab915b2bf..f182409ac593 100644 --- a/src/plugins/vmxnet3/input.c +++ b/src/plugins/vmxnet3/input.c @@ -269,7 +269,6 @@ vmxnet3_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, b0->flags = 0; b0->error = 0; b0->current_config_index = 0; - ASSERT (b0->current_length != 0); if (PREDICT_FALSE ((rx_comp->index & VMXNET3_RXCI_EOP) && (rx_comp->len & VMXNET3_RXCL_ERROR))) @@ -317,12 +316,19 @@ vmxnet3_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, else if (rx_comp->index & VMXNET3_RXCI_EOP) { /* end of segment */ - if (prev_b0) + if (PREDICT_TRUE (prev_b0 != 0)) { - prev_b0->flags |= VLIB_BUFFER_NEXT_PRESENT; - prev_b0->next_buffer = bi0; - hb->total_length_not_including_first_buffer += - b0->current_length; + if (PREDICT_TRUE (b0->current_length != 0)) + { + prev_b0->flags |= VLIB_BUFFER_NEXT_PRESENT; + prev_b0->next_buffer = bi0; + hb->total_length_not_including_first_buffer += + b0->current_length; + } + else + { + vlib_buffer_free_one (vm, bi0); + } prev_b0 = 0; got_packet = 1; } @@ -387,7 +393,7 @@ vmxnet3_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, ethernet_header_t *e = (ethernet_header_t *) hb->data; next[0] = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT; - if (!ethernet_frame_is_tagged (e->type)) + if (!ethernet_frame_is_tagged (ntohs (e->type))) vmxnet3_handle_offload (rx_comp, hb, gso_size); } diff --git a/src/plugins/vmxnet3/output.c b/src/plugins/vmxnet3/output.c index 81a1afb190c8..4c9b7093f737 100644 --- a/src/plugins/vmxnet3/output.c +++ b/src/plugins/vmxnet3/output.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include @@ -128,6 +130,7 @@ VNET_DEVICE_CLASS_TX_FN (vmxnet3_device_class) (vlib_main_t * vm, { u16 space_needed = 1, i; u32 gso_size = 0; + u32 l4_hdr_sz; vlib_buffer_t *b; u32 hdr_len = 0; @@ -193,8 +196,13 @@ VNET_DEVICE_CLASS_TX_FN (vmxnet3_device_class) (vlib_main_t * vm, */ ASSERT (vd->gso_enable == 1); gso_size = vnet_buffer2 (b0)->gso_size; - hdr_len = vnet_buffer (b0)->l4_hdr_offset + - sizeof (ethernet_header_t); + l4_hdr_sz = vnet_buffer2 (b0)->gso_l4_hdr_sz; + if (b0->flags & VNET_BUFFER_F_IS_IP6) + hdr_len = sizeof (ethernet_header_t) + sizeof (ip6_header_t) + + l4_hdr_sz; + else + hdr_len = sizeof (ethernet_header_t) + sizeof (ip4_header_t) + + l4_hdr_sz; } generation = txq->tx_ring.gen; @@ -202,9 +210,9 @@ VNET_DEVICE_CLASS_TX_FN (vmxnet3_device_class) (vlib_main_t * vm, } if (PREDICT_FALSE (gso_size != 0)) { - txd->flags[1] = hdr_len; - txd->flags[1] |= VMXNET3_TXF_OM (VMXNET3_OM_TSO); - txd->flags[0] |= VMXNET3_TXF_MSSCOF (gso_size); + txq->tx_desc[first_idx].flags[1] = hdr_len; + txq->tx_desc[first_idx].flags[1] |= VMXNET3_TXF_OM (VMXNET3_OM_TSO); + txq->tx_desc[first_idx].flags[0] |= VMXNET3_TXF_MSSCOF (gso_size); } txd->flags[1] |= VMXNET3_TXF_CQ | VMXNET3_TXF_EOP; asm volatile ("":::"memory"); diff --git a/src/plugins/vmxnet3/vmxnet3.c b/src/plugins/vmxnet3/vmxnet3.c index 731a34a99e1b..9209c3e2f906 100644 --- a/src/plugins/vmxnet3/vmxnet3.c +++ b/src/plugins/vmxnet3/vmxnet3.c @@ -809,7 +809,8 @@ vmxnet3_create_if (vlib_main_t * vm, vmxnet3_create_if_args_t * args) vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vd->hw_if_index); hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE; if (vd->gso_enable) - hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO; + hw->flags |= (VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO | + VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD); vnet_hw_interface_set_input_node (vnm, vd->hw_if_index, vmxnet3_input_node.index); diff --git a/src/plugins/wireguard/CMakeLists.txt b/src/plugins/wireguard/CMakeLists.txt index db5bb2d8910e..db74f9cdce03 100755 --- a/src/plugins/wireguard/CMakeLists.txt +++ b/src/plugins/wireguard/CMakeLists.txt @@ -30,6 +30,7 @@ add_vpp_plugin(wireguard wireguard_if.h wireguard_input.c wireguard_output_tun.c + wireguard_handoff.c wireguard_key.c wireguard_key.h wireguard_cli.c diff --git a/src/plugins/wireguard/README.md b/src/plugins/wireguard/README.md index a11356cfde2d..e313a32142ce 100755 --- a/src/plugins/wireguard/README.md +++ b/src/plugins/wireguard/README.md @@ -1,4 +1,4 @@ -# Wireguard vpp-plugin +# Wireguard vpp-plugin {#wireguard_plugin_doc} ## Overview This plugin is an implementation of [wireguard protocol](https://www.wireguard.com/) for VPP. It allows one to create secure VPN tunnels. diff --git a/src/plugins/wireguard/test/test_wireguard.py b/src/plugins/wireguard/test/test_wireguard.py index cd124f3e2460..cee1e938bb0a 100755 --- a/src/plugins/wireguard/test/test_wireguard.py +++ b/src/plugins/wireguard/test/test_wireguard.py @@ -1,15 +1,24 @@ #!/usr/bin/env python3 """ Wg tests """ +import datetime +import base64 + +from hashlib import blake2s from scapy.packet import Packet from scapy.packet import Raw -from scapy.layers.l2 import Ether +from scapy.layers.l2 import Ether, ARP from scapy.layers.inet import IP, UDP from scapy.contrib.wireguard import Wireguard, WireguardResponse, \ - WireguardInitiation -from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey + WireguardInitiation, WireguardTransport +from cryptography.hazmat.primitives.asymmetric.x25519 import \ + X25519PrivateKey, X25519PublicKey from cryptography.hazmat.primitives.serialization import Encoding, \ PrivateFormat, PublicFormat, NoEncryption +from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash +from cryptography.hazmat.primitives.hmac import HMAC +from cryptography.hazmat.backends import default_backend +from noise.connection import NoiseConnection, Keypair from vpp_ipip_tun_interface import VppIpIpTunInterface from vpp_interface import VppInterface @@ -25,41 +34,48 @@ """ +def private_key_bytes(k): + return k.private_bytes(Encoding.Raw, + PrivateFormat.Raw, + NoEncryption()) + + +def public_key_bytes(k): + return k.public_bytes(Encoding.Raw, + PublicFormat.Raw) + + class VppWgInterface(VppInterface): """ VPP WireGuard interface """ - def __init__(self, test, src, port, key=None): + def __init__(self, test, src, port): super(VppWgInterface, self).__init__(test) - self.key = key - if not self.key: - self.generate = True - else: - self.generate = False self.port = port self.src = src + self.private_key = X25519PrivateKey.generate() + self.public_key = self.private_key.public_key() + + def public_key_bytes(self): + return public_key_bytes(self.public_key) + + def private_key_bytes(self): + return private_key_bytes(self.private_key) def add_vpp_config(self): r = self.test.vapi.wireguard_interface_create(interface={ 'user_instance': 0xffffffff, 'port': self.port, 'src_ip': self.src, - 'private_key': self.key_bytes() + 'private_key': private_key_bytes(self.private_key), + 'generate_key': False }) self.set_sw_if_index(r.sw_if_index) self.test.registry.register(self, self.test.logger) return self - def key_bytes(self): - if self.key: - return self.key.private_bytes(Encoding.Raw, - PrivateFormat.Raw, - NoEncryption()) - else: - return bytearray(32) - def remove_vpp_config(self): self.test.vapi.wireguard_interface_delete( sw_if_index=self._sw_if_index) @@ -70,7 +86,7 @@ def query_vpp_config(self): if t.interface.sw_if_index == self._sw_if_index and \ str(t.interface.src_ip) == self.src and \ t.interface.port == self.port and \ - t.interface.private_key == self.key_bytes(): + t.interface.private_key == private_key_bytes(self.private_key): return True return False @@ -91,6 +107,10 @@ def find_route(test, prefix, table_id=0): return False +NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s" +NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com" + + class VppWgPeer(VppObject): def __init__(self, @@ -106,9 +126,12 @@ def __init__(self, self.port = port self.allowed_ips = allowed_ips self.persistent_keepalive = persistent_keepalive + + # remote peer's public self.private_key = X25519PrivateKey.generate() self.public_key = self.private_key.public_key() - self.hash = bytearray(16) + + self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME) def validate_routing(self): for a in self.allowed_ips: @@ -129,6 +152,7 @@ def add_vpp_config(self): 'sw_if_index': self.itf.sw_if_index, 'persistent_keepalive': self.persistent_keepalive}) self.index = rv.peer_index + self.receiver_index = self.index + 1 self._test.registry.register(self, self._test.logger) self.validate_routing() return self @@ -141,13 +165,7 @@ def object_id(self): return ("wireguard-peer-%s" % self.index) def public_key_bytes(self): - return self.public_key.public_bytes(Encoding.Raw, - PublicFormat.Raw) - - def private_key_bytes(self): - return self.private_key.private_bytes(Encoding.Raw, - PrivateFormat.Raw, - NoEncryption()) + return public_key_bytes(self.public_key) def query_vpp_config(self): peers = self._test.vapi.wireguard_peers_dump() @@ -167,6 +185,156 @@ def query_vpp_config(self): return True return False + def set_responder(self): + self.noise.set_as_responder() + + def mk_tunnel_header(self, tx_itf): + return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) / + IP(src=self.endpoint, dst=self.itf.src) / + UDP(sport=self.port, dport=self.itf.port)) + + def noise_init(self, public_key=None): + self.noise.set_prologue(NOISE_IDENTIFIER_NAME) + self.noise.set_psks(psk=bytes(bytearray(32))) + + if not public_key: + public_key = self.itf.public_key + + # local/this private + self.noise.set_keypair_from_private_bytes( + Keypair.STATIC, + private_key_bytes(self.private_key)) + # remote's public + self.noise.set_keypair_from_public_bytes( + Keypair.REMOTE_STATIC, + public_key_bytes(public_key)) + + self.noise.start_handshake() + + def mk_handshake(self, tx_itf, public_key=None): + self.noise.set_as_initiator() + self.noise_init(public_key) + + p = (Wireguard() / WireguardInitiation()) + + p[Wireguard].message_type = 1 + p[Wireguard].reserved_zero = 0 + p[WireguardInitiation].sender_index = self.receiver_index + + # some random data for the message + # lifted from the noise protocol's wireguard example + now = datetime.datetime.now() + tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()), + int(now.microsecond * 1e3)) + b = self.noise.write_message(payload=tai) + + # load noise into init message + p[WireguardInitiation].unencrypted_ephemeral = b[0:32] + p[WireguardInitiation].encrypted_static = b[32:80] + p[WireguardInitiation].encrypted_timestamp = b[80:108] + + # generate the mac1 hash + mac_key = blake2s(b'mac1----' + + self.itf.public_key_bytes()).digest() + p[WireguardInitiation].mac1 = blake2s(bytes(p)[0:116], + digest_size=16, + key=mac_key).digest() + p[WireguardInitiation].mac2 = bytearray(16) + + p = (self.mk_tunnel_header(tx_itf) / p) + + return p + + def verify_header(self, p): + self._test.assertEqual(p[IP].src, self.itf.src) + self._test.assertEqual(p[IP].dst, self.endpoint) + self._test.assertEqual(p[UDP].sport, self.itf.port) + self._test.assertEqual(p[UDP].dport, self.port) + self._test.assert_packet_checksums_valid(p) + + def consume_init(self, p, tx_itf): + self.noise.set_as_responder() + self.noise_init(self.itf.public_key) + self.verify_header(p) + + init = Wireguard(p[Raw]) + + self._test.assertEqual(init[Wireguard].message_type, 1) + self._test.assertEqual(init[Wireguard].reserved_zero, 0) + + self.sender = init[WireguardInitiation].sender_index + + # validate the hash + mac_key = blake2s(b'mac1----' + + public_key_bytes(self.public_key)).digest() + mac1 = blake2s(bytes(init)[0:-32], + digest_size=16, + key=mac_key).digest() + self._test.assertEqual(init[WireguardInitiation].mac1, mac1) + + # this passes only unencrypted_ephemeral, encrypted_static, + # encrypted_timestamp fields of the init + payload = self.noise.read_message(bytes(init)[8:-32]) + + # build the response + b = self.noise.write_message() + mac_key = blake2s(b'mac1----' + + public_key_bytes(self.itf.public_key)).digest() + resp = (Wireguard(message_type=2, reserved_zero=0) / + WireguardResponse(sender_index=self.receiver_index, + receiver_index=self.sender, + unencrypted_ephemeral=b[0:32], + encrypted_nothing=b[32:])) + mac1 = blake2s(bytes(resp)[:-32], + digest_size=16, + key=mac_key).digest() + resp[WireguardResponse].mac1 = mac1 + + resp = (self.mk_tunnel_header(tx_itf) / resp) + self._test.assertTrue(self.noise.handshake_finished) + + return resp + + def consume_response(self, p): + self.verify_header(p) + + resp = Wireguard(p[Raw]) + + self._test.assertEqual(resp[Wireguard].message_type, 2) + self._test.assertEqual(resp[Wireguard].reserved_zero, 0) + self._test.assertEqual(resp[WireguardResponse].receiver_index, + self.receiver_index) + + self.sender = resp[Wireguard].sender_index + + payload = self.noise.read_message(bytes(resp)[12:60]) + self._test.assertEqual(payload, b'') + self._test.assertTrue(self.noise.handshake_finished) + + def decrypt_transport(self, p): + self.verify_header(p) + + p = Wireguard(p[Raw]) + self._test.assertEqual(p[Wireguard].message_type, 4) + self._test.assertEqual(p[Wireguard].reserved_zero, 0) + self._test.assertEqual(p[WireguardTransport].receiver_index, + self.receiver_index) + + d = self.noise.decrypt( + p[WireguardTransport].encrypted_encapsulated_packet) + return d + + def encrypt_transport(self, p): + return self.noise.encrypt(bytes(p)) + + def validate_encapped(self, rxs, tx): + for rx in rxs: + rx = IP(self.decrypt_transport(rx)) + + # chech the oringial packet is present + self._test.assertEqual(rx[IP].dst, tx[IP].dst) + self._test.assertEqual(rx[IP].ttl, tx[IP].ttl-1) + class TestWg(VppTestCase): """ Wireguard Test Case """ @@ -192,6 +360,7 @@ def tearDownClass(cls): super(TestWg, cls).tearDownClass() def test_wg_interface(self): + """ Simple interface creation """ port = 12312 # Create interface @@ -204,7 +373,51 @@ def test_wg_interface(self): # delete interface wg0.remove_vpp_config() - def test_wg_peer(self): + def test_handshake_hash(self): + """ test hashing an init message """ + # a init packet generated by linux given the key below + h = "0100000098b9032b" \ + "55cc4b39e73c3d24" \ + "a2a1ab884b524a81" \ + "1808bb86640fb70d" \ + "e93154fec1879125" \ + "ab012624a27f0b75" \ + "c0a2582f438ddb5f" \ + "8e768af40b4ab444" \ + "02f9ff473e1b797e" \ + "80d39d93c5480c82" \ + "a3d4510f70396976" \ + "586fb67300a5167b" \ + "ae6ca3ff3dfd00eb" \ + "59be198810f5aa03" \ + "6abc243d2155ee4f" \ + "2336483900aef801" \ + "08752cd700000000" \ + "0000000000000000" \ + "00000000" + + b = bytearray.fromhex(h) + tgt = Wireguard(b) + + pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=") + pub = X25519PublicKey.from_public_bytes(pubb) + + self.assertEqual(pubb, public_key_bytes(pub)) + + # strip the macs and build a new packet + init = b[0:-32] + mac_key = blake2s(b'mac1----' + public_key_bytes(pub)).digest() + init += blake2s(init, + digest_size=16, + key=mac_key).digest() + init += b'\x00' * 16 + + act = Wireguard(init) + + self.assertEqual(tgt, act) + + def test_wg_peer_resp(self): + """ Send handshake response """ wg_output_node_name = '/err/wg-output-tun/' wg_input_node_name = '/err/wg-input/' @@ -213,16 +426,9 @@ def test_wg_peer(self): # Create interfaces wg0 = VppWgInterface(self, self.pg1.local_ip4, - port, - key=X25519PrivateKey.generate()).add_vpp_config() - wg1 = VppWgInterface(self, - self.pg2.local_ip4, - port+1).add_vpp_config() + port).add_vpp_config() wg0.admin_up() - wg1.admin_up() - - # Check peer counter - self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0) + wg0.config_ip4() self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -236,43 +442,206 @@ def test_wg_peer(self): self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1) # wait for the peer to send a handshake - capture = self.pg1.get_capture(1, timeout=2) - handshake = capture[0] + rx = self.pg1.get_capture(1, timeout=2) + + # consume the handshake in the noise protocol and + # generate the response + resp = peer_1.consume_init(rx[0], self.pg1) - self.assertEqual(handshake[IP].src, wg0.src) - self.assertEqual(handshake[IP].dst, peer_1.endpoint) - self.assertEqual(handshake[UDP].sport, wg0.port) - self.assertEqual(handshake[UDP].dport, peer_1.port) - handshake = Wireguard(handshake[Raw]) - self.assertEqual(handshake.message_type, 1) # "initiate") - init = handshake[WireguardInitiation] + # send the response, get keepalive + rxs = self.send_and_expect(self.pg1, [resp], self.pg1) + + for rx in rxs: + b = peer_1.decrypt_transport(rx) + self.assertEqual(0, len(b)) + + # send a packets that are routed into the tunnel + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw(b'\x00' * 80)) + + rxs = self.send_and_expect(self.pg0, p * 255, self.pg1) + + peer_1.validate_encapped(rxs, p) + + # send packets into the tunnel, expect to receive them on + # the other side + p = [(peer_1.mk_tunnel_header(self.pg1) / + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] + + rxs = self.send_and_expect(self.pg1, p, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + def test_wg_peer_init(self): + """ Send handshake init """ + wg_output_node_name = '/err/wg-output-tun/' + wg_input_node_name = '/err/wg-input/' + + port = 12323 + + # Create interfaces + wg0 = VppWgInterface(self, + self.pg1.local_ip4, + port).add_vpp_config() + wg0.admin_up() + wg0.config_ip4() + + peer_1 = VppWgPeer(self, + wg0, + self.pg1.remote_ip4, + port+1, + ["10.11.2.0/24", + "10.11.3.0/24"]).add_vpp_config() + self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1) # route a packet into the wg interface # use the allowed-ip prefix + # this is dropped because the peer is not initiated + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + + kp_error = wg_output_node_name + "Keypair error" + self.assertEqual(1, self.statistics.get_err_counter(kp_error)) + + # send a handsake from the peer with an invalid MAC + p = peer_1.mk_handshake(self.pg1) + p[WireguardInitiation].mac1 = b'foobar' + self.send_and_assert_no_replies(self.pg1, [p]) + self.assertEqual(1, self.statistics.get_err_counter( + wg_input_node_name + "Invalid MAC handshake")) + + # send a handsake from the peer but signed by the wrong key. + p = peer_1.mk_handshake(self.pg1, + X25519PrivateKey.generate().public_key()) + self.send_and_assert_no_replies(self.pg1, [p]) + self.assertEqual(1, self.statistics.get_err_counter( + wg_input_node_name + "Peer error")) + + # send a valid handsake init for which we expect a response + p = peer_1.mk_handshake(self.pg1) + + rx = self.send_and_expect(self.pg1, [p], self.pg1) + + peer_1.consume_response(rx[0]) + + # route a packet into the wg interface + # this is dropped because the peer is still not initiated p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / UDP(sport=555, dport=556) / Raw()) - # rx = self.send_and_expect(self.pg0, [p], self.pg1) - rx = self.send_and_assert_no_replies(self.pg0, [p]) + self.send_and_assert_no_replies(self.pg0, [p]) + self.assertEqual(2, self.statistics.get_err_counter(kp_error)) + + # send a data packet from the peer through the tunnel + # this completes the handshake + p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw()) + d = peer_1.encrypt_transport(p) + p = (peer_1.mk_tunnel_header(self.pg1) / + (Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport(receiver_index=peer_1.sender, + counter=0, + encrypted_encapsulated_packet=d))) + rxs = self.send_and_expect(self.pg1, [p], self.pg0) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + # send a packets that are routed into the tunnel + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw(b'\x00' * 80)) + + rxs = self.send_and_expect(self.pg0, p * 255, self.pg1) + + for rx in rxs: + rx = IP(peer_1.decrypt_transport(rx)) + + # chech the oringial packet is present + self.assertEqual(rx[IP].dst, p[IP].dst) + self.assertEqual(rx[IP].ttl, p[IP].ttl-1) + + # send packets into the tunnel, expect to receive them on + # the other side + p = [(peer_1.mk_tunnel_header(self.pg1) / + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii+1, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] - self.logger.info(self.vapi.cli("sh error")) - init_sent = wg_output_node_name + "Keypair error" - self.assertEqual(1, self.statistics.get_err_counter(init_sent)) + rxs = self.send_and_expect(self.pg1, p, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + peer_1.remove_vpp_config() + wg0.remove_vpp_config() + + def test_wg_multi_peer(self): + """ multiple peer setup """ + port = 12323 + + # Create interfaces + wg0 = VppWgInterface(self, + self.pg1.local_ip4, + port).add_vpp_config() + wg1 = VppWgInterface(self, + self.pg2.local_ip4, + port+1).add_vpp_config() + wg0.admin_up() + wg1.admin_up() + + # Check peer counter + self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() # Create many peers on sencond interface NUM_PEERS = 16 self.pg2.generate_remote_hosts(NUM_PEERS) self.pg2.configure_ipv4_neighbors() + self.pg1.generate_remote_hosts(NUM_PEERS) + self.pg1.configure_ipv4_neighbors() - peers = [] + peers_1 = [] + peers_2 = [] for i in range(NUM_PEERS): - peers.append(VppWgPeer(self, - wg1, - self.pg2.remote_hosts[i].ip4, - port+1+i, - ["10.10.%d.4/32" % i]).add_vpp_config()) - self.assertEqual(len(self.vapi.wireguard_peers_dump()), i+2) + peers_1.append(VppWgPeer(self, + wg0, + self.pg1.remote_hosts[i].ip4, + port+1+i, + ["10.0.%d.4/32" % i]).add_vpp_config()) + peers_2.append(VppWgPeer(self, + wg1, + self.pg2.remote_hosts[i].ip4, + port+100+i, + ["10.100.%d.4/32" % i]).add_vpp_config()) + + self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS*2) self.logger.info(self.vapi.cli("show wireguard peer")) self.logger.info(self.vapi.cli("show wireguard interface")) @@ -281,12 +650,99 @@ def test_wg_peer(self): self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0")) # remove peers - for p in peers: + for p in peers_1: self.assertTrue(p.query_vpp_config()) p.remove_vpp_config() + for p in peers_2: + self.assertTrue(p.query_vpp_config()) + p.remove_vpp_config() + + wg0.remove_vpp_config() + wg1.remove_vpp_config() + + +class WireguardHandoffTests(TestWg): + """ Wireguard Tests in multi worker setup """ + worker_config = "workers 2" + + def test_wg_peer_init(self): + """ Handoff """ + wg_output_node_name = '/err/wg-output-tun/' + wg_input_node_name = '/err/wg-input/' + + port = 12323 + + # Create interfaces + wg0 = VppWgInterface(self, + self.pg1.local_ip4, + port).add_vpp_config() + wg0.admin_up() + wg0.config_ip4() + + peer_1 = VppWgPeer(self, + wg0, + self.pg1.remote_ip4, + port+1, + ["10.11.2.0/24", + "10.11.3.0/24"]).add_vpp_config() self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1) - peer_1.remove_vpp_config() - self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0) + # send a valid handsake init for which we expect a response + p = peer_1.mk_handshake(self.pg1) + + rx = self.send_and_expect(self.pg1, [p], self.pg1) + + peer_1.consume_response(rx[0]) + + # send a data packet from the peer through the tunnel + # this completes the handshake and pins the peer to worker 0 + p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw()) + d = peer_1.encrypt_transport(p) + p = (peer_1.mk_tunnel_header(self.pg1) / + (Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport(receiver_index=peer_1.sender, + counter=0, + encrypted_encapsulated_packet=d))) + rxs = self.send_and_expect(self.pg1, [p], self.pg0, + worker=0) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + # send a packets that are routed into the tunnel + # and pins the peer tp worker 1 + pe = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw(b'\x00' * 80)) + rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1) + peer_1.validate_encapped(rxs, pe) + + # send packets into the tunnel, from the other worker + p = [(peer_1.mk_tunnel_header(self.pg1) / + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii+1, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] + + rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + # send a packets that are routed into the tunnel + # from owrker 0 + rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0) + + peer_1.validate_encapped(rxs, pe) + + peer_1.remove_vpp_config() wg0.remove_vpp_config() - # wg1.remove_vpp_config() diff --git a/src/plugins/wireguard/wireguard.c b/src/plugins/wireguard/wireguard.c index 00921811e4a6..9510a0ad385b 100755 --- a/src/plugins/wireguard/wireguard.c +++ b/src/plugins/wireguard/wireguard.c @@ -32,7 +32,17 @@ wg_init (vlib_main_t * vm) wg_main_t *wmp = &wg_main; wmp->vlib_main = vm; - wmp->peers = 0; + + wmp->in_fq_index = vlib_frame_queue_main_init (wg_input_node.index, 0); + wmp->out_fq_index = + vlib_frame_queue_main_init (wg_output_tun_node.index, 0); + + vlib_thread_main_t *tm = vlib_get_thread_main (); + + vec_validate_aligned (wmp->per_thread_data, tm->n_vlib_mains, + CLIB_CACHE_LINE_BYTES); + + wg_timer_wheel_init (); return (NULL); } diff --git a/src/plugins/wireguard/wireguard.h b/src/plugins/wireguard/wireguard.h index 70a692e602f2..2c892a374b8d 100755 --- a/src/plugins/wireguard/wireguard.h +++ b/src/plugins/wireguard/wireguard.h @@ -17,13 +17,17 @@ #include #include -#include +#include + +#define WG_DEFAULT_DATA_SIZE 2048 extern vlib_node_registration_t wg_input_node; extern vlib_node_registration_t wg_output_tun_node; - - +typedef struct wg_per_thread_data_t_ +{ + u8 data[WG_DEFAULT_DATA_SIZE]; +} wg_per_thread_data_t; typedef struct { /* convenience */ @@ -31,10 +35,14 @@ typedef struct u16 msg_id_base; - // Peers pool - wg_peer_t *peers; wg_index_table_t index_table; + u32 in_fq_index; + u32 out_fq_index; + + wg_per_thread_data_t *per_thread_data; + + tw_timer_wheel_16t_2w_512sl_t timer_wheel; } wg_main_t; extern wg_main_t wg_main; diff --git a/src/plugins/wireguard/wireguard_api.c b/src/plugins/wireguard/wireguard_api.c index e107cb56b4b1..27ed6ea05da6 100755 --- a/src/plugins/wireguard/wireguard_api.c +++ b/src/plugins/wireguard/wireguard_api.c @@ -39,7 +39,7 @@ static void wg_main_t *wmp = &wg_main; u8 private_key[NOISE_PUBLIC_KEY_LEN]; ip_address_t src; - u32 sw_if_index; + u32 sw_if_index = ~0; int rv = 0; ip_address_decode2 (&mp->interface.src_ip, &src); @@ -97,15 +97,17 @@ wireguard_if_send_details (index_t wgii, void *data) vl_api_wireguard_interface_details_t *rmp; wg_deatils_walk_t *ctx = data; const wg_if_t *wgi; + const noise_local_t *local; wgi = wg_if_get (wgii); + local = noise_local_get (wgi->local_idx); rmp = vl_msg_api_alloc_zero (sizeof (*rmp)); rmp->_vl_msg_id = htons (VL_API_WIREGUARD_INTERFACE_DETAILS + wg_main.msg_id_base); clib_memcpy (rmp->interface.private_key, - wgi->local.l_private, NOISE_PUBLIC_KEY_LEN); + local->l_private, NOISE_PUBLIC_KEY_LEN); rmp->interface.sw_if_index = htonl (wgi->sw_if_index); rmp->interface.port = htons (wgi->port); ip_address_encode2 (&wgi->src_ip, &rmp->interface.src_ip); @@ -140,7 +142,7 @@ vl_api_wireguard_peer_add_t_handler (vl_api_wireguard_peer_add_t * mp) { vl_api_wireguard_peer_add_reply_t *rmp; wg_main_t *wmp = &wg_main; - index_t peeri; + index_t peeri = INDEX_INVALID; int ii, rv = 0; ip_address_t endpoint; diff --git a/src/plugins/wireguard/wireguard_cookie.c b/src/plugins/wireguard/wireguard_cookie.c index aa476f792a1e..f54ce715906a 100755 --- a/src/plugins/wireguard/wireguard_cookie.c +++ b/src/plugins/wireguard/wireguard_cookie.c @@ -86,7 +86,7 @@ cookie_checker_validate_macs (vlib_main_t * vm, cookie_checker_t * cc, len = len - sizeof (message_macs_t); cookie_macs_mac1 (&our_cm, buf, len, cc->cc_mac1_key); - /* If mac1 is invald, we want to drop the packet */ + /* If mac1 is invalid, we want to drop the packet */ if (clib_memcmp (our_cm.mac1, cm->mac1, COOKIE_MAC_SIZE) != 0) return INVALID_MAC; diff --git a/src/plugins/wireguard/wireguard_handoff.c b/src/plugins/wireguard/wireguard_handoff.c new file mode 100644 index 000000000000..8a2efa508b35 --- /dev/null +++ b/src/plugins/wireguard/wireguard_handoff.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2020 Doc.ai and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#define foreach_wg_handoff_error \ +_(CONGESTION_DROP, "congestion drop") + +typedef enum +{ +#define _(sym,str) WG_HANDOFF_ERROR_##sym, + foreach_wg_handoff_error +#undef _ + HANDOFF_N_ERROR, +} ipsec_handoff_error_t; + +static char *wg_handoff_error_strings[] = { +#define _(sym,string) string, + foreach_wg_handoff_error +#undef _ +}; + +typedef enum +{ + WG_HANDOFF_HANDSHAKE, + WG_HANDOFF_INP_DATA, + WG_HANDOFF_OUT_TUN, +} wg_handoff_mode_t; + +typedef struct wg_handoff_trace_t_ +{ + u32 next_worker_index; + index_t peer; +} wg_handoff_trace_t; + +static u8 * +format_wg_handoff_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + wg_handoff_trace_t *t = va_arg (*args, wg_handoff_trace_t *); + + s = format (s, "next-worker %d peer %d", t->next_worker_index, t->peer); + + return s; +} + +static_always_inline uword +wg_handoff (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u32 fq_index, wg_handoff_mode_t mode) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u16 thread_indices[VLIB_FRAME_SIZE], *ti; + u32 n_enq, n_left_from, *from; + wg_main_t *wmp; + + wmp = &wg_main; + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + vlib_get_buffers (vm, from, bufs, n_left_from); + + b = bufs; + ti = thread_indices; + + while (n_left_from > 0) + { + const wg_peer_t *peer; + index_t peeri = INDEX_INVALID; + + if (PREDICT_FALSE (mode == WG_HANDOFF_HANDSHAKE)) + { + ti[0] = 0; + } + else if (mode == WG_HANDOFF_INP_DATA) + { + message_data_t *data = vlib_buffer_get_current (b[0]); + u32 *entry = + wg_index_table_lookup (&wmp->index_table, data->receiver_index); + peeri = *entry; + peer = wg_peer_get (peeri); + + ti[0] = peer->input_thread_index; + } + else + { + peeri = + wg_peer_get_by_adj_index (vnet_buffer (b[0])-> + ip.adj_index[VLIB_TX]); + peer = wg_peer_get (peeri); + ti[0] = peer->output_thread_index; + } + + if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + { + wg_handoff_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->next_worker_index = ti[0]; + t->peer = peeri; + } + + n_left_from -= 1; + ti += 1; + b += 1; + } + + n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, + thread_indices, frame->n_vectors, 1); + + if (n_enq < frame->n_vectors) + vlib_node_increment_counter (vm, node->node_index, + WG_HANDOFF_ERROR_CONGESTION_DROP, + frame->n_vectors - n_enq); + + return n_enq; +} + +VLIB_NODE_FN (wg_handshake_handoff) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + wg_main_t *wmp = &wg_main; + + return wg_handoff (vm, node, from_frame, wmp->in_fq_index, + WG_HANDOFF_HANDSHAKE); +} + +VLIB_NODE_FN (wg_input_data_handoff) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + wg_main_t *wmp = &wg_main; + + return wg_handoff (vm, node, from_frame, wmp->in_fq_index, + WG_HANDOFF_INP_DATA); +} + +VLIB_NODE_FN (wg_output_tun_handoff) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + wg_main_t *wmp = &wg_main; + + return wg_handoff (vm, node, from_frame, wmp->out_fq_index, + WG_HANDOFF_OUT_TUN); +} + +VLIB_REGISTER_NODE (wg_handshake_handoff) = +{ + .name = "wg-handshake-handoff",.vector_size = sizeof (u32),.format_trace = + format_wg_handoff_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARRAY_LEN (wg_handoff_error_strings),.error_strings = + wg_handoff_error_strings,.n_next_nodes = 1,.next_nodes = + { + [0] = "error-drop",} +,}; + +VLIB_REGISTER_NODE (wg_input_data_handoff) = +{ + .name = "wg-input-data-handoff",.vector_size = sizeof (u32),.format_trace = + format_wg_handoff_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARRAY_LEN (wg_handoff_error_strings),.error_strings = + wg_handoff_error_strings,.n_next_nodes = 1,.next_nodes = + { + [0] = "error-drop",} +,}; + +VLIB_REGISTER_NODE (wg_output_tun_handoff) = +{ + .name = "wg-output-tun-handoff",.vector_size = sizeof (u32),.format_trace = + format_wg_handoff_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARRAY_LEN (wg_handoff_error_strings),.error_strings = + wg_handoff_error_strings,.n_next_nodes = 1,.next_nodes = + { + [0] = "error-drop",} +,}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/wireguard/wireguard_if.c b/src/plugins/wireguard/wireguard_if.c index ff8ed35477ee..7509923a1bfc 100644 --- a/src/plugins/wireguard/wireguard_if.c +++ b/src/plugins/wireguard/wireguard_if.c @@ -5,6 +5,7 @@ #include #include #include +#include /* pool of interfaces */ wg_if_t *wg_if_pool; @@ -30,23 +31,33 @@ format_wg_if (u8 * s, va_list * args) { index_t wgii = va_arg (*args, u32); wg_if_t *wgi = wg_if_get (wgii); + noise_local_t *local = noise_local_get (wgi->local_idx); u8 key[NOISE_KEY_LEN_BASE64]; - key_to_base64 (wgi->local.l_private, NOISE_PUBLIC_KEY_LEN, key); s = format (s, "[%d] %U src:%U port:%d", wgii, format_vnet_sw_if_index_name, vnet_get_main (), wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port); - key_to_base64 (wgi->local.l_private, NOISE_PUBLIC_KEY_LEN, key); + key_to_base64 (local->l_private, NOISE_PUBLIC_KEY_LEN, key); s = format (s, " private-key:%s", key); + s = + format (s, " %U", format_hex_bytes, local->l_private, + NOISE_PUBLIC_KEY_LEN); - key_to_base64 (wgi->local.l_public, NOISE_PUBLIC_KEY_LEN, key); + key_to_base64 (local->l_public, NOISE_PUBLIC_KEY_LEN, key); s = format (s, " public-key:%s", key); + s = + format (s, " %U", format_hex_bytes, local->l_public, + NOISE_PUBLIC_KEY_LEN); + + s = format (s, " mac-key: %U", format_hex_bytes, + &wgi->cookie_checker.cc_mac1_key, NOISE_PUBLIC_KEY_LEN); + return (s); } @@ -62,23 +73,28 @@ wg_if_find_by_sw_if_index (u32 sw_if_index) return (ti); } +static walk_rc_t +wg_if_find_peer_by_public_key (index_t peeri, void *data) +{ + uint8_t *public = data; + wg_peer_t *peer = wg_peer_get (peeri); + + if (!memcmp (peer->remote.r_public, public, NOISE_PUBLIC_KEY_LEN)) + return (WALK_STOP); + return (WALK_CONTINUE); +} + static noise_remote_t * -wg_remote_get (uint8_t public[NOISE_PUBLIC_KEY_LEN]) +wg_remote_get (const uint8_t public[NOISE_PUBLIC_KEY_LEN]) { - wg_main_t *wmp = &wg_main; - wg_peer_t *peer = NULL; - wg_peer_t *peer_iter; - /* *INDENT-OFF* */ - pool_foreach (peer_iter, wmp->peers, - ({ - if (!memcmp (peer_iter->remote.r_public, public, NOISE_PUBLIC_KEY_LEN)) - { - peer = peer_iter; - break; - } - })); - /* *INDENT-ON* */ - return peer ? &peer->remote : NULL; + index_t peeri; + + peeri = wg_peer_walk (wg_if_find_peer_by_public_key, (void *) public); + + if (INDEX_INVALID != peeri) + return &wg_peer_get (peeri)->remote; + + return NULL; } static uint32_t @@ -213,6 +229,7 @@ wg_if_create (u32 user_instance, u32 instance, hw_if_index; vnet_hw_interface_t *hi; wg_if_t *wg_if; + noise_local_t *local; ASSERT (sw_if_indexp); @@ -226,6 +243,24 @@ wg_if_create (u32 user_instance, if (instance == ~0) return VNET_API_ERROR_INVALID_REGISTRATION; + /* *INDENT-OFF* */ + struct noise_upcall upcall = { + .u_remote_get = wg_remote_get, + .u_index_set = wg_index_set, + .u_index_drop = wg_index_drop, + }; + /* *INDENT-ON* */ + + pool_get (noise_local_pool, local); + + noise_local_init (local, &upcall); + if (!noise_local_set_private (local, private_key)) + { + pool_put (noise_local_pool, local); + wg_if_instance_free (instance); + return VNET_API_ERROR_INVALID_REGISTRATION; + } + pool_get (wg_if_pool, wg_if); /* tunnel index (or instance) */ @@ -235,23 +270,14 @@ wg_if_create (u32 user_instance, if (~0 == wg_if->user_instance) wg_if->user_instance = t_idx; - udp_dst_port_info_t *pi = udp_get_dst_port_info (&udp_main, port, UDP_IP4); - if (pi) - return (VNET_API_ERROR_VALUE_EXIST); udp_register_dst_port (vlib_get_main (), port, wg_input_node.index, 1); vec_validate_init_empty (wg_if_index_by_port, port, INDEX_INVALID); wg_if_index_by_port[port] = wg_if - wg_if_pool; wg_if->port = port; - struct noise_upcall upcall; - upcall.u_remote_get = wg_remote_get; - upcall.u_index_set = wg_index_set; - upcall.u_index_drop = wg_index_drop; - - noise_local_init (&wg_if->local, &upcall); - noise_local_set_private (&wg_if->local, private_key); - cookie_checker_update (&wg_if->cookie_checker, wg_if->local.l_public); + wg_if->local_idx = local - noise_local_pool; + cookie_checker_update (&wg_if->cookie_checker, local->l_public); hw_if_index = vnet_register_interface (vnm, wg_if_device_class.index, @@ -280,18 +306,20 @@ wg_if_delete (u32 sw_if_index) vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); if (hw == 0 || hw->dev_class_index != wg_if_device_class.index) - return VNET_API_ERROR_INVALID_SW_IF_INDEX; + return VNET_API_ERROR_INVALID_VALUE; wg_if_t *wg_if; wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index)); if (NULL == wg_if) - return VNET_API_ERROR_INVALID_SW_IF_INDEX; + return VNET_API_ERROR_INVALID_SW_IF_INDEX_2; - if (wg_if_instance_free (hw->dev_instance) < 0) - return VNET_API_ERROR_INVALID_SW_IF_INDEX; + if (wg_if_instance_free (wg_if->user_instance) < 0) + return VNET_API_ERROR_INVALID_VALUE_2; + udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1); wg_if_index_by_port[wg_if->port] = INDEX_INVALID; vnet_delete_hw_interface (vnm, hw->hw_if_index); + pool_put_index (noise_local_pool, wg_if->local_idx); pool_put (wg_if_pool, wg_if); return 0; @@ -331,7 +359,7 @@ wg_if_walk (wg_if_walk_cb_t fn, void *data) /* *INDENT-ON* */ } -void +index_t wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data) { index_t peeri, val; @@ -340,9 +368,11 @@ wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data) hash_foreach (peeri, val, wgi->peers, { if (WALK_STOP == fn(wgi, peeri, data)) - break; + return peeri; }); /* *INDENT-ON* */ + + return INDEX_INVALID; } diff --git a/src/plugins/wireguard/wireguard_if.h b/src/plugins/wireguard/wireguard_if.h index 9e6b6190e0e0..d8c2a87dc71e 100644 --- a/src/plugins/wireguard/wireguard_if.h +++ b/src/plugins/wireguard/wireguard_if.h @@ -25,7 +25,8 @@ typedef struct wg_if_t_ u32 sw_if_index; // Interface params - noise_local_t local; + /* noise_local_pool elt index */ + u32 local_idx; cookie_checker_t cookie_checker; u16 port; @@ -52,7 +53,7 @@ void wg_if_walk (wg_if_walk_cb_t fn, void *data); typedef walk_rc_t (*wg_if_peer_walk_cb_t) (wg_if_t * wgi, index_t peeri, void *data); -void wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data); +index_t wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data); void wg_if_peer_add (wg_if_t * wgi, index_t peeri); void wg_if_peer_remove (wg_if_t * wgi, index_t peeri); diff --git a/src/plugins/wireguard/wireguard_input.c b/src/plugins/wireguard/wireguard_input.c index 832ad031daa0..b15c265cdac0 100755 --- a/src/plugins/wireguard/wireguard_input.c +++ b/src/plugins/wireguard/wireguard_input.c @@ -30,6 +30,7 @@ _(DECRYPTION, "Failed during decryption") \ _(KEEPALIVE_SEND, "Failed while sending Keepalive") \ _(HANDSHAKE_SEND, "Failed while sending Handshake") \ + _(TOO_BIG, "Packet too big") \ _(UNDEFINED, "Undefined error") typedef enum @@ -51,7 +52,7 @@ typedef struct message_type_t type; u16 current_length; bool is_keepalive; - + index_t peer; } wg_input_trace_t; u8 * @@ -79,6 +80,7 @@ format_wg_input_trace (u8 * s, va_list * args) s = format (s, "WG input: \n"); s = format (s, " Type: %U\n", format_wg_message_type, t->type); + s = format (s, " peer: %d\n", t->peer); s = format (s, " Length: %d\n", t->current_length); s = format (s, " Keepalive: %s", t->is_keepalive ? "true" : "false"); @@ -87,6 +89,8 @@ format_wg_input_trace (u8 * s, va_list * args) typedef enum { + WG_INPUT_NEXT_HANDOFF_HANDSHAKE, + WG_INPUT_NEXT_HANDOFF_DATA, WG_INPUT_NEXT_IP4_INPUT, WG_INPUT_NEXT_PUNT, WG_INPUT_NEXT_ERROR, @@ -106,6 +110,8 @@ typedef enum static wg_input_error_t wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) { + ASSERT (vm->thread_index == 0); + enum cookie_mac_state mac_state; bool packet_needs_cookie; bool under_load; @@ -129,17 +135,15 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) if (NULL == wg_if) return WG_INPUT_ERROR_INTERFACE; - if (header->type == MESSAGE_HANDSHAKE_COOKIE) + if (PREDICT_FALSE (header->type == MESSAGE_HANDSHAKE_COOKIE)) { message_handshake_cookie_t *packet = (message_handshake_cookie_t *) current_b_data; u32 *entry = wg_index_table_lookup (&wmp->index_table, packet->receiver_index); if (entry) - { - peer = pool_elt_at_index (wmp->peers, *entry); - } - if (!peer) + peer = wg_peer_get (*entry); + else return WG_INPUT_ERROR_PEER; // TODO: Implement cookie_maker_consume_payload @@ -178,17 +182,17 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) // TODO: Add processing } noise_remote_t *rp; - if (noise_consume_initiation - (wmp->vlib_main, &wg_if->local, &rp, message->sender_index, - message->unencrypted_ephemeral, message->encrypted_static, - message->encrypted_timestamp)) + (vm, noise_local_get (wg_if->local_idx), &rp, + message->sender_index, message->unencrypted_ephemeral, + message->encrypted_static, message->encrypted_timestamp)) { - peer = pool_elt_at_index (wmp->peers, rp->r_peer_idx); + peer = wg_peer_get (rp->r_peer_idx); + } + else + { + return WG_INPUT_ERROR_PEER; } - - if (!peer) - return WG_INPUT_ERROR_PEER; // set_peer_address (peer, ip4_src, udp_src_port); if (PREDICT_FALSE (!wg_send_handshake_response (vm, peer))) @@ -203,15 +207,18 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) message_handshake_response_t *resp = current_b_data; u32 *entry = wg_index_table_lookup (&wmp->index_table, resp->receiver_index); - if (entry) + + if (PREDICT_TRUE (entry != NULL)) { - peer = pool_elt_at_index (wmp->peers, *entry); - if (!peer || peer->is_dead) + peer = wg_peer_get (*entry); + if (peer->is_dead) return WG_INPUT_ERROR_PEER; } + else + return WG_INPUT_ERROR_PEER; if (!noise_consume_response - (wmp->vlib_main, &peer->remote, resp->sender_index, + (vm, &peer->remote, resp->sender_index, resp->receiver_index, resp->unencrypted_ephemeral, resp->encrypted_nothing)) { @@ -223,8 +230,9 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) } // set_peer_address (peer, ip4_src, udp_src_port); - if (noise_remote_begin_session (wmp->vlib_main, &peer->remote)) + if (noise_remote_begin_session (vm, &peer->remote)) { + wg_timers_session_derived (peer); wg_timers_handshake_complete (peer); if (PREDICT_FALSE (!wg_send_keepalive (vm, peer))) @@ -272,6 +280,7 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, u32 *from; vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; u16 nexts[VLIB_FRAME_SIZE], *next; + u32 thread_index = vm->thread_index; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -289,120 +298,132 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, next[0] = WG_INPUT_NEXT_PUNT; header_type = ((message_header_t *) vlib_buffer_get_current (b[0]))->type; + u32 *peer_idx; - switch (header_type) + if (PREDICT_TRUE (header_type == MESSAGE_DATA)) { - case MESSAGE_HANDSHAKE_INITIATION: - case MESSAGE_HANDSHAKE_RESPONSE: - case MESSAGE_HANDSHAKE_COOKIE: - { - wg_input_error_t ret = wg_handshake_process (vm, wmp, b[0]); - if (ret != WG_INPUT_ERROR_NONE) - { - next[0] = WG_INPUT_NEXT_ERROR; - b[0]->error = node->errors[ret]; - } - break; - } - case MESSAGE_DATA: - { - message_data_t *data = vlib_buffer_get_current (b[0]); - u32 *entry = - wg_index_table_lookup (&wmp->index_table, data->receiver_index); - - if (entry) - { - peer = pool_elt_at_index (wmp->peers, *entry); - if (!peer) - { - next[0] = WG_INPUT_NEXT_ERROR; - b[0]->error = node->errors[WG_INPUT_ERROR_PEER]; - goto out; - } - } + message_data_t *data = vlib_buffer_get_current (b[0]); - u16 encr_len = b[0]->current_length - sizeof (message_data_t); - u16 decr_len = encr_len - NOISE_AUTHTAG_LEN; - u8 *decr_data = clib_mem_alloc (decr_len); + peer_idx = wg_index_table_lookup (&wmp->index_table, + data->receiver_index); - enum noise_state_crypt state_cr = - noise_remote_decrypt (wmp->vlib_main, - &peer->remote, - data->receiver_index, - data->counter, - data->encrypted_data, - encr_len, - decr_data); + if (peer_idx) + { + peer = wg_peer_get (*peer_idx); + } + else + { + next[0] = WG_INPUT_NEXT_ERROR; + b[0]->error = node->errors[WG_INPUT_ERROR_PEER]; + goto out; + } - switch (state_cr) - { - case SC_OK: - break; - case SC_CONN_RESET: - wg_timers_handshake_complete (peer); - break; - case SC_KEEP_KEY_FRESH: - if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false))) - { - vlib_node_increment_counter (vm, wg_input_node.index, - WG_INPUT_ERROR_HANDSHAKE_SEND, - 1); - } - break; - case SC_FAILED: - next[0] = WG_INPUT_NEXT_ERROR; - b[0]->error = node->errors[WG_INPUT_ERROR_DECRYPTION]; - goto out; - default: - break; - } + if (PREDICT_FALSE (~0 == peer->input_thread_index)) + { + /* this is the first packet to use this peer, claim the peer + * for this thread. + */ + clib_atomic_cmp_and_swap (&peer->input_thread_index, ~0, + wg_peer_assign_thread (thread_index)); + } - clib_memcpy (vlib_buffer_get_current (b[0]), decr_data, decr_len); - b[0]->current_length = decr_len; - b[0]->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + if (PREDICT_TRUE (thread_index != peer->input_thread_index)) + { + next[0] = WG_INPUT_NEXT_HANDOFF_DATA; + goto next; + } - clib_mem_free (decr_data); + u16 encr_len = b[0]->current_length - sizeof (message_data_t); + u16 decr_len = encr_len - NOISE_AUTHTAG_LEN; + if (PREDICT_FALSE (decr_len >= WG_DEFAULT_DATA_SIZE)) + { + b[0]->error = node->errors[WG_INPUT_ERROR_TOO_BIG]; + goto out; + } - wg_timers_any_authenticated_packet_received (peer); - wg_timers_any_authenticated_packet_traversal (peer); + u8 *decr_data = wmp->per_thread_data[thread_index].data; - if (decr_len == 0) - { - is_keepalive = true; - goto out; - } + enum noise_state_crypt state_cr = noise_remote_decrypt (vm, + &peer->remote, + data->receiver_index, + data->counter, + data->encrypted_data, + encr_len, + decr_data); - wg_timers_data_received (peer); + if (PREDICT_FALSE (state_cr == SC_CONN_RESET)) + { + wg_timers_handshake_complete (peer); + } + else if (PREDICT_FALSE (state_cr == SC_KEEP_KEY_FRESH)) + { + wg_send_handshake_from_mt (*peer_idx, false); + } + else if (PREDICT_FALSE (state_cr == SC_FAILED)) + { + next[0] = WG_INPUT_NEXT_ERROR; + b[0]->error = node->errors[WG_INPUT_ERROR_DECRYPTION]; + goto out; + } - ip4_header_t *iph = vlib_buffer_get_current (b[0]); + clib_memcpy (vlib_buffer_get_current (b[0]), decr_data, decr_len); + b[0]->current_length = decr_len; + b[0]->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; - const wg_peer_allowed_ip_t *allowed_ip; - bool allowed = false; + wg_timers_any_authenticated_packet_received (peer); + wg_timers_any_authenticated_packet_traversal (peer); - /* - * we could make this into an ACL, but the expectation - * is that there aren't many allowed IPs and thus a linear - * walk is fater than an ACL - */ - vec_foreach (allowed_ip, peer->allowed_ips) + /* Keepalive packet has zero length */ + if (decr_len == 0) { - if (fib_prefix_is_cover_addr_4 (&allowed_ip->prefix, - &iph->src_address)) - { - allowed = true; - break; - } + is_keepalive = true; + goto out; } - if (allowed) + + wg_timers_data_received (peer); + + ip4_header_t *iph = vlib_buffer_get_current (b[0]); + + const wg_peer_allowed_ip_t *allowed_ip; + bool allowed = false; + + /* + * we could make this into an ACL, but the expectation + * is that there aren't many allowed IPs and thus a linear + * walk is fater than an ACL + */ + vec_foreach (allowed_ip, peer->allowed_ips) + { + if (fib_prefix_is_cover_addr_4 (&allowed_ip->prefix, + &iph->src_address)) { - vnet_buffer (b[0])->sw_if_index[VLIB_RX] = - peer->wg_sw_if_index; - next[0] = WG_INPUT_NEXT_IP4_INPUT; + allowed = true; + break; } - break; } - default: - break; + if (allowed) + { + vnet_buffer (b[0])->sw_if_index[VLIB_RX] = peer->wg_sw_if_index; + next[0] = WG_INPUT_NEXT_IP4_INPUT; + } + } + else + { + peer_idx = NULL; + + /* Handshake packets should be processed in main thread */ + if (thread_index != 0) + { + next[0] = WG_INPUT_NEXT_HANDOFF_HANDSHAKE; + goto next; + } + + wg_input_error_t ret = wg_handshake_process (vm, wmp, b[0]); + if (ret != WG_INPUT_ERROR_NONE) + { + next[0] = WG_INPUT_NEXT_ERROR; + b[0]->error = node->errors[ret]; + } } out: @@ -413,7 +434,9 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, t->type = header_type; t->current_length = b[0]->current_length; t->is_keepalive = is_keepalive; + t->peer = peer_idx ? *peer_idx : INDEX_INVALID; } + next: n_left_from -= 1; next += 1; b += 1; @@ -435,6 +458,8 @@ VLIB_REGISTER_NODE (wg_input_node) = .n_next_nodes = WG_INPUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { + [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg-handshake-handoff", + [WG_INPUT_NEXT_HANDOFF_DATA] = "wg-input-data-handoff", [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", [WG_INPUT_NEXT_PUNT] = "error-punt", [WG_INPUT_NEXT_ERROR] = "error-drop", diff --git a/src/plugins/wireguard/wireguard_noise.c b/src/plugins/wireguard/wireguard_noise.c index 666618a2a513..850be2c86c8b 100755 --- a/src/plugins/wireguard/wireguard_noise.c +++ b/src/plugins/wireguard/wireguard_noise.c @@ -26,6 +26,8 @@ * <- e, ee, se, psk, {} */ +noise_local_t *noise_local_pool; + /* Private functions */ static noise_keypair_t *noise_remote_keypair_allocate (noise_remote_t *); static void noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t *, @@ -80,81 +82,31 @@ noise_local_set_private (noise_local_t * l, const uint8_t private[NOISE_PUBLIC_KEY_LEN]) { clib_memcpy (l->l_private, private, NOISE_PUBLIC_KEY_LEN); - l->l_has_identity = curve25519_gen_public (l->l_public, private); - return l->l_has_identity; -} - -bool -noise_local_keys (noise_local_t * l, uint8_t public[NOISE_PUBLIC_KEY_LEN], - uint8_t private[NOISE_PUBLIC_KEY_LEN]) -{ - if (l->l_has_identity) - { - if (public != NULL) - clib_memcpy (public, l->l_public, NOISE_PUBLIC_KEY_LEN); - if (private != NULL) - clib_memcpy (private, l->l_private, NOISE_PUBLIC_KEY_LEN); - } - else - { - return false; - } - return true; + return curve25519_gen_public (l->l_public, private); } void noise_remote_init (noise_remote_t * r, uint32_t peer_pool_idx, const uint8_t public[NOISE_PUBLIC_KEY_LEN], - noise_local_t * l) + u32 noise_local_idx) { clib_memset (r, 0, sizeof (*r)); clib_memcpy (r->r_public, public, NOISE_PUBLIC_KEY_LEN); + clib_rwlock_init (&r->r_keypair_lock); r->r_peer_idx = peer_pool_idx; - - ASSERT (l != NULL); - r->r_local = l; + r->r_local_idx = noise_local_idx; r->r_handshake.hs_state = HS_ZEROED; - noise_remote_precompute (r); -} -bool -noise_remote_set_psk (noise_remote_t * r, - uint8_t psk[NOISE_SYMMETRIC_KEY_LEN]) -{ - int same; - same = !clib_memcmp (r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN); - if (!same) - { - clib_memcpy (r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN); - } - return same == 0; -} - -bool -noise_remote_keys (noise_remote_t * r, uint8_t public[NOISE_PUBLIC_KEY_LEN], - uint8_t psk[NOISE_SYMMETRIC_KEY_LEN]) -{ - static uint8_t null_psk[NOISE_SYMMETRIC_KEY_LEN]; - int ret; - - if (public != NULL) - clib_memcpy (public, r->r_public, NOISE_PUBLIC_KEY_LEN); - - if (psk != NULL) - clib_memcpy (psk, r->r_psk, NOISE_SYMMETRIC_KEY_LEN); - ret = clib_memcmp (r->r_psk, null_psk, NOISE_SYMMETRIC_KEY_LEN); - - return ret; + noise_remote_precompute (r); } void noise_remote_precompute (noise_remote_t * r) { - noise_local_t *l = r->r_local; - if (!l->l_has_identity) - clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN); - else if (!curve25519_gen_shared (r->r_ss, l->l_private, r->r_public)) + noise_local_t *l = noise_local_get (r->r_local_idx); + + if (!curve25519_gen_shared (r->r_ss, l->l_private, r->r_public)) clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN); noise_remote_handshake_index_drop (r); @@ -169,7 +121,7 @@ noise_create_initiation (vlib_main_t * vm, noise_remote_t * r, uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]) { noise_handshake_t *hs = &r->r_handshake; - noise_local_t *l = r->r_local; + noise_local_t *l = noise_local_get (r->r_local_idx); uint8_t _key[NOISE_SYMMETRIC_KEY_LEN]; uint32_t key_idx; uint8_t *key; @@ -180,8 +132,6 @@ noise_create_initiation (vlib_main_t * vm, noise_remote_t * r, NOISE_SYMMETRIC_KEY_LEN); key = vnet_crypto_get_key (key_idx)->data; - if (!l->l_has_identity) - goto error; noise_param_init (hs->hs_ck, hs->hs_hash, r->r_public); /* e */ @@ -211,8 +161,8 @@ noise_create_initiation (vlib_main_t * vm, noise_remote_t * r, *s_idx = hs->hs_local_index; ret = true; error: - vnet_crypto_key_del (vm, key_idx); secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); + vnet_crypto_key_del (vm, key_idx); return ret; } @@ -239,8 +189,6 @@ noise_consume_initiation (vlib_main_t * vm, noise_local_t * l, NOISE_SYMMETRIC_KEY_LEN); key = vnet_crypto_get_key (key_idx)->data; - if (!l->l_has_identity) - goto error; noise_param_init (hs.hs_ck, hs.hs_hash, l->l_public); /* e */ @@ -294,9 +242,10 @@ noise_consume_initiation (vlib_main_t * vm, noise_local_t * l, r->r_handshake = hs; *rp = r; ret = true; + error: - vnet_crypto_key_del (vm, key_idx); secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); + vnet_crypto_key_del (vm, key_idx); secure_zero_memory (&hs, sizeof (hs)); return ret; } @@ -348,8 +297,8 @@ noise_create_response (vlib_main_t * vm, noise_remote_t * r, uint32_t * s_idx, *s_idx = hs->hs_local_index; ret = true; error: - vnet_crypto_key_del (vm, key_idx); secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); + vnet_crypto_key_del (vm, key_idx); secure_zero_memory (e, NOISE_PUBLIC_KEY_LEN); return ret; } @@ -359,7 +308,7 @@ noise_consume_response (vlib_main_t * vm, noise_remote_t * r, uint32_t s_idx, uint32_t r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], uint8_t en[0 + NOISE_AUTHTAG_LEN]) { - noise_local_t *l = r->r_local; + noise_local_t *l = noise_local_get (r->r_local_idx); noise_handshake_t hs; uint8_t _key[NOISE_SYMMETRIC_KEY_LEN]; uint8_t preshared_key[NOISE_PUBLIC_KEY_LEN]; @@ -372,9 +321,6 @@ noise_consume_response (vlib_main_t * vm, noise_remote_t * r, uint32_t s_idx, NOISE_SYMMETRIC_KEY_LEN); key = vnet_crypto_get_key (key_idx)->data; - if (!l->l_has_identity) - goto error; - hs = r->r_handshake; clib_memcpy (preshared_key, r->r_psk, NOISE_SYMMETRIC_KEY_LEN); @@ -412,9 +358,9 @@ noise_consume_response (vlib_main_t * vm, noise_remote_t * r, uint32_t s_idx, ret = true; } error: - vnet_crypto_key_del (vm, key_idx); secure_zero_memory (&hs, sizeof (hs)); secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); + vnet_crypto_key_del (vm, key_idx); return ret; } @@ -460,6 +406,7 @@ noise_remote_begin_session (vlib_main_t * vm, noise_remote_t * r) clib_memset (&kp.kp_ctr, 0, sizeof (kp.kp_ctr)); /* Now we need to add_new_keypair */ + clib_rwlock_writer_lock (&r->r_keypair_lock); next = r->r_next; current = r->r_current; previous = r->r_previous; @@ -491,7 +438,10 @@ noise_remote_begin_session (vlib_main_t * vm, noise_remote_t * r) r->r_next = noise_remote_keypair_allocate (r); *r->r_next = kp; } + clib_rwlock_writer_unlock (&r->r_keypair_lock); + secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake)); + secure_zero_memory (&kp, sizeof (kp)); return true; } @@ -502,21 +452,25 @@ noise_remote_clear (vlib_main_t * vm, noise_remote_t * r) noise_remote_handshake_index_drop (r); secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake)); + clib_rwlock_writer_lock (&r->r_keypair_lock); noise_remote_keypair_free (vm, r, &r->r_next); noise_remote_keypair_free (vm, r, &r->r_current); noise_remote_keypair_free (vm, r, &r->r_previous); r->r_next = NULL; r->r_current = NULL; r->r_previous = NULL; + clib_rwlock_writer_unlock (&r->r_keypair_lock); } void noise_remote_expire_current (noise_remote_t * r) { + clib_rwlock_writer_lock (&r->r_keypair_lock); if (r->r_next != NULL) r->r_next->kp_valid = 0; if (r->r_current != NULL) r->r_current->kp_valid = 0; + clib_rwlock_writer_unlock (&r->r_keypair_lock); } bool @@ -525,6 +479,7 @@ noise_remote_ready (noise_remote_t * r) noise_keypair_t *kp; int ret; + clib_rwlock_reader_lock (&r->r_keypair_lock); if ((kp = r->r_current) == NULL || !kp->kp_valid || wg_birthdate_has_expired (kp->kp_birthdate, REJECT_AFTER_TIME) || @@ -533,10 +488,11 @@ noise_remote_ready (noise_remote_t * r) ret = false; else ret = true; + clib_rwlock_reader_unlock (&r->r_keypair_lock); return ret; } -static void +static bool chacha20poly1305_calc (vlib_main_t * vm, u8 * src, u32 src_len, @@ -547,34 +503,41 @@ chacha20poly1305_calc (vlib_main_t * vm, vnet_crypto_op_id_t op_id, vnet_crypto_key_index_t key_index) { + vnet_crypto_op_t _op, *op = &_op; u8 iv[12]; + u8 tag_[NOISE_AUTHTAG_LEN] = { }; + u8 src_[] = { }; + clib_memset (iv, 0, 12); clib_memcpy (iv + 4, &nonce, sizeof (nonce)); - vnet_crypto_op_t _op, *op = &_op; + vnet_crypto_op_init (op, op_id); - u8 _tag[16] = { }; + op->tag_len = NOISE_AUTHTAG_LEN; if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC) { - clib_memcpy (_tag, src + src_len - NOISE_AUTHTAG_LEN, - NOISE_AUTHTAG_LEN); + op->tag = src + src_len - NOISE_AUTHTAG_LEN; src_len -= NOISE_AUTHTAG_LEN; } - vnet_crypto_op_init (op, op_id); - op->key_index = key_index; - op->src = src; - op->dst = dst; + else + op->tag = tag_; + + op->src = !src ? src_ : src; op->len = src_len; + + op->dst = dst; + op->key_index = key_index; op->aad = aad; op->aad_len = aad_len; op->iv = iv; - op->tag_len = NOISE_AUTHTAG_LEN; - op->tag = _tag; + vnet_crypto_process_ops (vm, op, 1); if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC) { clib_memcpy (dst + src_len, op->tag, NOISE_AUTHTAG_LEN); } + + return (op->status == VNET_CRYPTO_OP_STATUS_COMPLETED); } enum noise_state_crypt @@ -585,6 +548,7 @@ noise_remote_encrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t * r_idx, noise_keypair_t *kp; enum noise_state_crypt ret = SC_FAILED; + clib_rwlock_reader_lock (&r->r_keypair_lock); if ((kp = r->r_current) == NULL) goto error; @@ -624,6 +588,7 @@ noise_remote_encrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t * r_idx, ret = SC_OK; error: + clib_rwlock_reader_unlock (&r->r_keypair_lock); return ret; } @@ -634,6 +599,7 @@ noise_remote_decrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t r_idx, { noise_keypair_t *kp; enum noise_state_crypt ret = SC_FAILED; + clib_rwlock_reader_lock (&r->r_keypair_lock); if (r->r_current != NULL && r->r_current->kp_local_index == r_idx) { @@ -663,9 +629,10 @@ noise_remote_decrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t r_idx, /* Decrypt, then validate the counter. We don't want to validate the * counter before decrypting as we do not know the message is authentic * prior to decryption. */ - chacha20poly1305_calc (vm, src, srclen, dst, NULL, 0, nonce, - VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, - kp->kp_recv_index); + if (!chacha20poly1305_calc (vm, src, srclen, dst, NULL, 0, nonce, + VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, + kp->kp_recv_index)) + goto error; if (!noise_counter_recv (&kp->kp_ctr, nonce)) goto error; @@ -674,18 +641,26 @@ noise_remote_decrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t r_idx, * next keypair into current. If we do slide the next keypair in, then * we skip the REKEY_AFTER_TIME_RECV check. This is safe to do as a * data packet can't confirm a session that we are an INITIATOR of. */ - if (kp == r->r_next && kp->kp_local_index == r_idx) + if (kp == r->r_next) { - noise_remote_keypair_free (vm, r, &r->r_previous); - r->r_previous = r->r_current; - r->r_current = r->r_next; - r->r_next = NULL; + clib_rwlock_reader_unlock (&r->r_keypair_lock); + clib_rwlock_writer_lock (&r->r_keypair_lock); + if (kp == r->r_next && kp->kp_local_index == r_idx) + { + noise_remote_keypair_free (vm, r, &r->r_previous); + r->r_previous = r->r_current; + r->r_current = r->r_next; + r->r_next = NULL; - ret = SC_CONN_RESET; - goto error; + ret = SC_CONN_RESET; + clib_rwlock_writer_unlock (&r->r_keypair_lock); + clib_rwlock_reader_lock (&r->r_keypair_lock); + goto error; + } + clib_rwlock_writer_unlock (&r->r_keypair_lock); + clib_rwlock_reader_lock (&r->r_keypair_lock); } - /* Similar to when we encrypt, we want to notify the caller when we * are approaching our tolerances. We notify if: * - we're the initiator and the current keypair is older than @@ -700,6 +675,7 @@ noise_remote_decrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t r_idx, ret = SC_OK; error: + clib_rwlock_reader_unlock (&r->r_keypair_lock); return ret; } @@ -717,7 +693,8 @@ static void noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t * r, noise_keypair_t ** kp) { - struct noise_upcall *u = &r->r_local->l_upcall; + noise_local_t *local = noise_local_get (r->r_local_idx); + struct noise_upcall *u = &local->l_upcall; if (*kp) { u->u_index_drop ((*kp)->kp_local_index); @@ -730,7 +707,8 @@ noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t * r, static uint32_t noise_remote_handshake_index_get (noise_remote_t * r) { - struct noise_upcall *u = &r->r_local->l_upcall; + noise_local_t *local = noise_local_get (r->r_local_idx); + struct noise_upcall *u = &local->l_upcall; return u->u_index_set (r); } @@ -738,7 +716,8 @@ static void noise_remote_handshake_index_drop (noise_remote_t * r) { noise_handshake_t *hs = &r->r_handshake; - struct noise_upcall *u = &r->r_local->l_upcall; + noise_local_t *local = noise_local_get (r->r_local_idx); + struct noise_upcall *u = &local->l_upcall; if (hs->hs_state != HS_ZEROED) u->u_index_drop (hs->hs_local_index); } @@ -746,7 +725,8 @@ noise_remote_handshake_index_drop (noise_remote_t * r) static uint64_t noise_counter_send (noise_counter_t * ctr) { - uint64_t ret = ctr->c_send++; + uint64_t ret; + ret = ctr->c_send++; return ret; } @@ -757,7 +737,6 @@ noise_counter_recv (noise_counter_t * ctr, uint64_t recv) unsigned long bit; bool ret = false; - /* Check that the recv counter is valid */ if (ctr->c_recv >= REJECT_AFTER_MESSAGES || recv >= REJECT_AFTER_MESSAGES) goto error; @@ -931,8 +910,9 @@ noise_msg_decrypt (vlib_main_t * vm, uint8_t * dst, uint8_t * src, uint8_t hash[NOISE_HASH_LEN]) { /* Nonce always zero for Noise_IK */ - chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0, - VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, key_idx); + if (!chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0, + VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, key_idx)) + return false; noise_mix_hash (hash, src, src_len); return true; } diff --git a/src/plugins/wireguard/wireguard_noise.h b/src/plugins/wireguard/wireguard_noise.h index 1f6804c59ca3..5b5a88fa2509 100755 --- a/src/plugins/wireguard/wireguard_noise.h +++ b/src/plugins/wireguard/wireguard_noise.h @@ -100,7 +100,7 @@ typedef struct noise_remote { uint32_t r_peer_idx; uint8_t r_public[NOISE_PUBLIC_KEY_LEN]; - noise_local_t *r_local; + uint32_t r_local_idx; uint8_t r_ss[NOISE_PUBLIC_KEY_LEN]; noise_handshake_t r_handshake; @@ -108,37 +108,40 @@ typedef struct noise_remote uint8_t r_timestamp[NOISE_TIMESTAMP_LEN]; f64 r_last_init; + clib_rwlock_t r_keypair_lock; noise_keypair_t *r_next, *r_current, *r_previous; } noise_remote_t; typedef struct noise_local { - bool l_has_identity; uint8_t l_public[NOISE_PUBLIC_KEY_LEN]; uint8_t l_private[NOISE_PUBLIC_KEY_LEN]; struct noise_upcall { void *u_arg; - noise_remote_t *(*u_remote_get) (uint8_t[NOISE_PUBLIC_KEY_LEN]); + noise_remote_t *(*u_remote_get) (const uint8_t[NOISE_PUBLIC_KEY_LEN]); uint32_t (*u_index_set) (noise_remote_t *); void (*u_index_drop) (uint32_t); } l_upcall; } noise_local_t; +/* pool of noise_local */ +extern noise_local_t *noise_local_pool; + /* Set/Get noise parameters */ +static_always_inline noise_local_t * +noise_local_get (uint32_t locali) +{ + return (pool_elt_at_index (noise_local_pool, locali)); +} + void noise_local_init (noise_local_t *, struct noise_upcall *); bool noise_local_set_private (noise_local_t *, const uint8_t[NOISE_PUBLIC_KEY_LEN]); -bool noise_local_keys (noise_local_t *, uint8_t[NOISE_PUBLIC_KEY_LEN], - uint8_t[NOISE_PUBLIC_KEY_LEN]); void noise_remote_init (noise_remote_t *, uint32_t, - const uint8_t[NOISE_PUBLIC_KEY_LEN], noise_local_t *); -bool noise_remote_set_psk (noise_remote_t *, - uint8_t[NOISE_SYMMETRIC_KEY_LEN]); -bool noise_remote_keys (noise_remote_t *, uint8_t[NOISE_PUBLIC_KEY_LEN], - uint8_t[NOISE_SYMMETRIC_KEY_LEN]); + const uint8_t[NOISE_PUBLIC_KEY_LEN], uint32_t); /* Should be called anytime noise_local_set_private is called */ void noise_remote_precompute (noise_remote_t *); diff --git a/src/plugins/wireguard/wireguard_output_tun.c b/src/plugins/wireguard/wireguard_output_tun.c index daec7a4a2f19..9a8710b77db3 100755 --- a/src/plugins/wireguard/wireguard_output_tun.c +++ b/src/plugins/wireguard/wireguard_output_tun.c @@ -15,10 +15,6 @@ #include #include -#include -#include -#include -#include #include #include @@ -28,19 +24,8 @@ _(NONE, "No error") \ _(PEER, "Peer error") \ _(KEYPAIR, "Keypair error") \ - _(HANDSHAKE_SEND, "Handshake sending failed") \ _(TOO_BIG, "packet too big") \ -#define WG_OUTPUT_SCRATCH_SIZE 2048 - -typedef struct wg_output_scratch_t_ -{ - u8 scratch[WG_OUTPUT_SCRATCH_SIZE]; -} wg_output_scratch_t; - -/* Cache line aligned per-thread scratch space */ -static wg_output_scratch_t *wg_output_scratchs; - typedef enum { #define _(sym,str) WG_OUTPUT_ERROR_##sym, @@ -58,6 +43,7 @@ static char *wg_output_error_strings[] = { typedef enum { WG_OUTPUT_NEXT_ERROR, + WG_OUTPUT_NEXT_HANDOFF, WG_OUTPUT_NEXT_INTERFACE_OUTPUT, WG_OUTPUT_N_NEXT, } wg_output_next_t; @@ -65,6 +51,7 @@ typedef enum typedef struct { ip4_udp_header_t hdr; + index_t peer; } wg_output_tun_trace_t; u8 * @@ -87,7 +74,8 @@ format_wg_output_tun_trace (u8 * s, va_list * args) wg_output_tun_trace_t *t = va_arg (*args, wg_output_tun_trace_t *); - s = format (s, "Encrypted packet: %U\n", format_ip4_udp_header, &t->hdr); + s = format (s, "peer: %d\n", t->peer); + s = format (s, " Encrypted packet: %U", format_ip4_udp_header, &t->hdr); return s; } @@ -109,20 +97,21 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, vlib_get_buffers (vm, from, bufs, n_left_from); wg_main_t *wmp = &wg_main; - u32 handsh_fails = 0; wg_peer_t *peer = NULL; while (n_left_from > 0) { ip4_udp_header_t *hdr = vlib_buffer_get_current (b[0]); - u8 *plain_data = vlib_buffer_get_current (b[0]) + sizeof (ip4_header_t); + u8 *plain_data = (vlib_buffer_get_current (b[0]) + + sizeof (ip4_udp_header_t)); u16 plain_data_len = clib_net_to_host_u16 (((ip4_header_t *) plain_data)->length); + index_t peeri; next[0] = WG_OUTPUT_NEXT_ERROR; - - peer = + peeri = wg_peer_get_by_adj_index (vnet_buffer (b[0])->ip.adj_index[VLIB_TX]); + peer = wg_peer_get (peeri); if (!peer || peer->is_dead) { @@ -130,22 +119,35 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, goto out; } + if (PREDICT_FALSE (~0 == peer->output_thread_index)) + { + /* this is the first packet to use this peer, claim the peer + * for this thread. + */ + clib_atomic_cmp_and_swap (&peer->output_thread_index, ~0, + wg_peer_assign_thread (thread_index)); + } + + if (PREDICT_TRUE (thread_index != peer->output_thread_index)) + { + next[0] = WG_OUTPUT_NEXT_HANDOFF; + goto next; + } + if (PREDICT_FALSE (!peer->remote.r_current)) { - if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false))) - handsh_fails++; + wg_send_handshake_from_mt (peeri, false); b[0]->error = node->errors[WG_OUTPUT_ERROR_KEYPAIR]; goto out; } - size_t encrypted_packet_len = message_data_len (plain_data_len); /* * Ensure there is enough space to write the encrypted data * into the packet */ - if (PREDICT_FALSE (encrypted_packet_len > WG_OUTPUT_SCRATCH_SIZE) || - PREDICT_FALSE ((b[0]->current_data + encrypted_packet_len) < + if (PREDICT_FALSE (encrypted_packet_len >= WG_DEFAULT_DATA_SIZE) || + PREDICT_FALSE ((b[0]->current_data + encrypted_packet_len) >= vlib_buffer_get_default_data_size (vm))) { b[0]->error = node->errors[WG_OUTPUT_ERROR_TOO_BIG]; @@ -153,35 +155,29 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, } message_data_t *encrypted_packet = - (message_data_t *) wg_output_scratchs[thread_index].scratch; + (message_data_t *) wmp->per_thread_data[thread_index].data; enum noise_state_crypt state; state = - noise_remote_encrypt (wmp->vlib_main, + noise_remote_encrypt (vm, &peer->remote, &encrypted_packet->receiver_index, &encrypted_packet->counter, plain_data, plain_data_len, encrypted_packet->encrypted_data); - switch (state) + + if (PREDICT_FALSE (state == SC_KEEP_KEY_FRESH)) + { + wg_send_handshake_from_mt (peeri, false); + } + else if (PREDICT_FALSE (state == SC_FAILED)) { - case SC_OK: - break; - case SC_KEEP_KEY_FRESH: - if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false))) - handsh_fails++; - break; - case SC_FAILED: //TODO: Maybe wrong - if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false))) - handsh_fails++; - clib_mem_free (encrypted_packet); + wg_send_handshake_from_mt (peeri, false); goto out; - default: - break; } - // Here we are sure that can send packet to next node. + /* Here we are sure that can send packet to next node */ next[0] = WG_OUTPUT_NEXT_INTERFACE_OUTPUT; encrypted_packet->header.type = MESSAGE_DATA; @@ -194,9 +190,9 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, ip4_header_set_len_w_chksum (&hdr->ip4, clib_host_to_net_u16 (b[0]->current_length)); - wg_timers_any_authenticated_packet_traversal (peer); wg_timers_any_authenticated_packet_sent (peer); wg_timers_data_sent (peer); + wg_timers_any_authenticated_packet_traversal (peer); out: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) @@ -205,17 +201,15 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, wg_output_tun_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t)); t->hdr = *hdr; + t->peer = peeri; } + next: n_left_from -= 1; next += 1; b += 1; } vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); - - vlib_node_increment_counter (vm, node->node_index, - WG_OUTPUT_ERROR_HANDSHAKE_SEND, handsh_fails); - return frame->n_vectors; } @@ -230,24 +224,13 @@ VLIB_REGISTER_NODE (wg_output_tun_node) = .error_strings = wg_output_error_strings, .n_next_nodes = WG_OUTPUT_N_NEXT, .next_nodes = { + [WG_OUTPUT_NEXT_HANDOFF] = "wg-output-tun-handoff", [WG_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx", [WG_OUTPUT_NEXT_ERROR] = "error-drop", }, }; /* *INDENT-ON* */ -static clib_error_t * -wireguard_output_module_init (vlib_main_t * vm) -{ - vlib_thread_main_t *tm = vlib_get_thread_main (); - - vec_validate_aligned (wg_output_scratchs, tm->n_vlib_mains, - CLIB_CACHE_LINE_BYTES); - return (NULL); -} - -VLIB_INIT_FUNCTION (wireguard_output_module_init); - /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/wireguard/wireguard_peer.c b/src/plugins/wireguard/wireguard_peer.c index 0dcc4e20e41e..b41118f83d1f 100755 --- a/src/plugins/wireguard/wireguard_peer.c +++ b/src/plugins/wireguard/wireguard_peer.c @@ -23,15 +23,10 @@ #include static fib_source_t wg_fib_source; +wg_peer_t *wg_peer_pool; index_t *wg_peer_by_adj_index; -wg_peer_t * -wg_peer_get (index_t peeri) -{ - return (pool_elt_at_index (wg_main.peers, peeri)); -} - static void wg_peer_endpoint_reset (wg_peer_endpoint_t * ep) { @@ -82,7 +77,11 @@ static void wg_peer_clear (vlib_main_t * vm, wg_peer_t * peer) { wg_timers_stop (peer); - noise_remote_clear (vm, &peer->remote); + for (int i = 0; i < WG_N_TIMERS; i++) + { + peer->timers[i] = ~0; + } + peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1); clib_memset (&peer->cookie_maker, 0, sizeof (peer->cookie_maker)); @@ -97,9 +96,18 @@ wg_peer_clear (vlib_main_t * vm, wg_peer_t * peer) } wg_peer_fib_flush (peer); + peer->input_thread_index = ~0; + peer->output_thread_index = ~0; peer->adj_index = INDEX_INVALID; + peer->timer_wheel = 0; peer->persistent_keepalive_interval = 0; peer->timer_handshake_attempts = 0; + peer->last_sent_packet = 0; + peer->last_received_packet = 0; + peer->session_derived = 0; + peer->rehandshake_started = 0; + peer->new_handshake_interval_tick = 0; + peer->rehandshake_interval_tick = 0; peer->timer_need_another_keepalive = false; peer->is_dead = true; vec_free (peer->allowed_ips); @@ -108,7 +116,7 @@ wg_peer_clear (vlib_main_t * vm, wg_peer_t * peer) static void wg_peer_init (vlib_main_t * vm, wg_peer_t * peer) { - wg_timers_init (peer, vlib_time_now (vm)); + peer->adj_index = INDEX_INVALID; wg_peer_clear (vm, peer); } @@ -205,8 +213,9 @@ wg_peer_fill (vlib_main_t * vm, wg_peer_t * peer, wg_peer_endpoint_init (&peer->dst, dst, port); peer->table_id = table_id; - peer->persistent_keepalive_interval = persistent_keepalive_interval; peer->wg_sw_if_index = wg_sw_if_index; + peer->timer_wheel = &wg_main.timer_wheel; + peer->persistent_keepalive_interval = persistent_keepalive_interval; peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1); peer->is_dead = false; @@ -230,7 +239,7 @@ wg_peer_fill (vlib_main_t * vm, wg_peer_t * peer, vec_validate_init_empty (wg_peer_by_adj_index, peer->adj_index, INDEX_INVALID); - wg_peer_by_adj_index[peer->adj_index] = peer - wg_main.peers; + wg_peer_by_adj_index[peer->adj_index] = peer - wg_peer_pool; adj_nbr_midchain_update_rewrite (peer->adj_index, NULL, @@ -280,7 +289,7 @@ wg_peer_add (u32 tun_sw_if_index, return (VNET_API_ERROR_INVALID_SW_IF_INDEX); /* *INDENT-OFF* */ - pool_foreach (peer, wg_main.peers, + pool_foreach (peer, wg_peer_pool, ({ if (!memcmp (peer->remote.r_public, public_key, NOISE_PUBLIC_KEY_LEN)) { @@ -289,10 +298,10 @@ wg_peer_add (u32 tun_sw_if_index, })); /* *INDENT-ON* */ - if (pool_elts (wg_main.peers) > MAX_PEERS) + if (pool_elts (wg_peer_pool) > MAX_PEERS) return (VNET_API_ERROR_LIMIT_EXCEEDED); - pool_get (wg_main.peers, peer); + pool_get (wg_peer_pool, peer); wg_peer_init (vm, peer); @@ -302,12 +311,12 @@ wg_peer_add (u32 tun_sw_if_index, if (rv) { wg_peer_clear (vm, peer); - pool_put (wg_main.peers, peer); + pool_put (wg_peer_pool, peer); return (rv); } - noise_remote_init (&peer->remote, peer - wg_main.peers, public_key, - &wg_if->local); + noise_remote_init (&peer->remote, peer - wg_peer_pool, public_key, + wg_if->local_idx); cookie_maker_init (&peer->cookie_maker, public_key); if (peer->persistent_keepalive_interval != 0) @@ -315,7 +324,7 @@ wg_peer_add (u32 tun_sw_if_index, wg_send_keepalive (vm, peer); } - *peer_index = peer - wg_main.peers; + *peer_index = peer - wg_peer_pool; wg_if_peer_add (wg_if, *peer_index); return (0); @@ -328,34 +337,37 @@ wg_peer_remove (index_t peeri) wg_peer_t *peer = NULL; wg_if_t *wgi; - if (pool_is_free_index (wmp->peers, peeri)) + if (pool_is_free_index (wg_peer_pool, peeri)) return VNET_API_ERROR_NO_SUCH_ENTRY; - peer = pool_elt_at_index (wmp->peers, peeri); + peer = pool_elt_at_index (wg_peer_pool, peeri); wgi = wg_if_get (wg_if_find_by_sw_if_index (peer->wg_sw_if_index)); wg_if_peer_remove (wgi, peeri); vnet_feature_enable_disable ("ip4-output", "wg-output-tun", peer->wg_sw_if_index, 0, 0, 0); + + noise_remote_clear (wmp->vlib_main, &peer->remote); wg_peer_clear (wmp->vlib_main, peer); - pool_put (wmp->peers, peer); + pool_put (wg_peer_pool, peer); return (0); } -void +index_t wg_peer_walk (wg_peer_walk_cb_t fn, void *data) { index_t peeri; /* *INDENT-OFF* */ - pool_foreach_index(peeri, wg_main.peers, + pool_foreach_index(peeri, wg_peer_pool, { if (WALK_STOP == fn(peeri, data)) - break; + return peeri; }); /* *INDENT-ON* */ + return INDEX_INVALID; } static u8 * @@ -380,15 +392,16 @@ format_wg_peer (u8 * s, va_list * va) peer = wg_peer_get (peeri); key_to_base64 (peer->remote.r_public, NOISE_PUBLIC_KEY_LEN, key); - s = format (s, "[%d] key:%=45s endpoint:[%U->%U] %U keep-alive:%d adj:%d", + s = format (s, "[%d] endpoint:[%U->%U] %U keep-alive:%d adj:%d", peeri, - key, format_wg_peer_endpoint, &peer->src, format_wg_peer_endpoint, &peer->dst, format_vnet_sw_if_index_name, vnet_get_main (), peer->wg_sw_if_index, peer->persistent_keepalive_interval, peer->adj_index); - + s = format (s, "\n key:%=s %U", + key, format_hex_bytes, peer->remote.r_public, + NOISE_PUBLIC_KEY_LEN); s = format (s, "\n allowed-ips:"); vec_foreach (allowed_ip, peer->allowed_ips) { @@ -401,8 +414,13 @@ format_wg_peer (u8 * s, va_list * va) static clib_error_t * wg_peer_module_init (vlib_main_t * vm) { - wg_fib_source = fib_source_allocate ("wireguard", 0xb0, // - FIB_SOURCE_BH_SIMPLE); + /* + * use a priority better than interface source, so that + * if the same subnet is added to the wg interface and is + * used as an allowed IP, then the wireguard soueced prefix + * wins and traffic is routed to the endpoint rather than dropped + */ + wg_fib_source = fib_source_allocate ("wireguard", 0x2, FIB_SOURCE_BH_API); return (NULL); } diff --git a/src/plugins/wireguard/wireguard_peer.h b/src/plugins/wireguard/wireguard_peer.h index 99c73f3a0edd..009a6f67aeb4 100755 --- a/src/plugins/wireguard/wireguard_peer.h +++ b/src/plugins/wireguard/wireguard_peer.h @@ -49,6 +49,9 @@ typedef struct wg_peer noise_remote_t remote; cookie_maker_t cookie_maker; + u32 input_thread_index; + u32 output_thread_index; + /* Peer addresses */ wg_peer_endpoint_t dst; wg_peer_endpoint_t src; @@ -65,11 +68,22 @@ typedef struct wg_peer u32 wg_sw_if_index; /* Timers */ - tw_timer_wheel_16t_2w_512sl_t timer_wheel; + tw_timer_wheel_16t_2w_512sl_t *timer_wheel; u32 timers[WG_N_TIMERS]; u32 timer_handshake_attempts; u16 persistent_keepalive_interval; + + /* Timestamps */ f64 last_sent_handshake; + f64 last_sent_packet; + f64 last_received_packet; + f64 session_derived; + f64 rehandshake_started; + + /* Variable intervals */ + u32 new_handshake_interval_tick; + u32 rehandshake_interval_tick; + bool timer_need_another_keepalive; bool is_dead; @@ -91,10 +105,9 @@ int wg_peer_add (u32 tun_sw_if_index, int wg_peer_remove (u32 peer_index); typedef walk_rc_t (*wg_peer_walk_cb_t) (index_t peeri, void *arg); -void wg_peer_walk (wg_peer_walk_cb_t fn, void *data); +index_t wg_peer_walk (wg_peer_walk_cb_t fn, void *data); u8 *format_wg_peer (u8 * s, va_list * va); -wg_peer_t *wg_peer_get (index_t peeri); walk_rc_t wg_peer_if_admin_state_change (wg_if_t * wgi, index_t peeri, void *data); @@ -104,11 +117,30 @@ walk_rc_t wg_peer_if_table_change (wg_if_t * wgi, index_t peeri, void *data); * Expoed for the data-plane */ extern index_t *wg_peer_by_adj_index; +extern wg_peer_t *wg_peer_pool; static inline wg_peer_t * +wg_peer_get (index_t peeri) +{ + return (pool_elt_at_index (wg_peer_pool, peeri)); +} + +static inline index_t wg_peer_get_by_adj_index (index_t ai) { - return wg_peer_get (wg_peer_by_adj_index[ai]); + return (wg_peer_by_adj_index[ai]); +} + +/* + * Makes choice for thread_id should be assigned. +*/ +static inline u32 +wg_peer_assign_thread (u32 thread_id) +{ + return ((thread_id) ? thread_id + : (vlib_num_workers ()? + ((unix_time_now_nsec () % vlib_num_workers ()) + + 1) : thread_id)); } #endif // __included_wg_peer_h__ diff --git a/src/plugins/wireguard/wireguard_send.c b/src/plugins/wireguard/wireguard_send.c index a5d8aaf69009..2e29a9b4b763 100755 --- a/src/plugins/wireguard/wireguard_send.c +++ b/src/plugins/wireguard/wireguard_send.c @@ -14,13 +14,11 @@ */ #include -#include -#include -#include #include #include #include #include +#include #include #include @@ -86,7 +84,8 @@ wg_create_buffer (vlib_main_t * vm, bool wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry) { - wg_main_t *wmp = &wg_main; + ASSERT (vm->thread_index == 0); + message_handshake_initiation_t packet; if (!is_retry) @@ -94,41 +93,73 @@ wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry) if (!wg_birthdate_has_expired (peer->last_sent_handshake, REKEY_TIMEOUT) || peer->is_dead) - { - return true; - } - if (noise_create_initiation (wmp->vlib_main, + return true; + + if (noise_create_initiation (vm, &peer->remote, &packet.sender_index, packet.unencrypted_ephemeral, packet.encrypted_static, packet.encrypted_timestamp)) { - f64 now = vlib_time_now (vm); packet.header.type = MESSAGE_HANDSHAKE_INITIATION; cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet, sizeof (packet)); - wg_timers_any_authenticated_packet_traversal (peer); wg_timers_any_authenticated_packet_sent (peer); - peer->last_sent_handshake = now; wg_timers_handshake_initiated (peer); + wg_timers_any_authenticated_packet_traversal (peer); + + peer->last_sent_handshake = vlib_time_now (vm); } else return false; + u32 bi0 = 0; if (!wg_create_buffer (vm, peer, (u8 *) & packet, sizeof (packet), &bi0)) return false; - ip46_enqueue_packet (vm, bi0, false); + ip46_enqueue_packet (vm, bi0, false); return true; } +typedef struct +{ + u32 peer_idx; + bool is_retry; +} wg_send_args_t; + +static void * +wg_send_handshake_thread_fn (void *arg) +{ + wg_send_args_t *a = arg; + + wg_main_t *wmp = &wg_main; + wg_peer_t *peer = wg_peer_get (a->peer_idx); + + wg_send_handshake (wmp->vlib_main, peer, a->is_retry); + return 0; +} + +void +wg_send_handshake_from_mt (u32 peer_idx, bool is_retry) +{ + wg_send_args_t a = { + .peer_idx = peer_idx, + .is_retry = is_retry, + }; + + vl_api_rpc_call_main_thread (wg_send_handshake_thread_fn, + (u8 *) & a, sizeof (a)); +} + bool wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer) { - wg_main_t *wmp = &wg_main; + ASSERT (vm->thread_index == 0); + u32 size_of_packet = message_data_len (0); - message_data_t *packet = clib_mem_alloc (size_of_packet); + message_data_t *packet = + (message_data_t *) wg_main.per_thread_data[vm->thread_index].data; u32 bi0 = 0; bool ret = true; enum noise_state_crypt state; @@ -140,23 +171,21 @@ wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer) } state = - noise_remote_encrypt (wmp->vlib_main, + noise_remote_encrypt (vm, &peer->remote, &packet->receiver_index, &packet->counter, NULL, 0, packet->encrypted_data); - switch (state) + + if (PREDICT_FALSE (state == SC_KEEP_KEY_FRESH)) { - case SC_OK: - break; - case SC_KEEP_KEY_FRESH: wg_send_handshake (vm, peer, false); - break; - case SC_FAILED: + } + else if (PREDICT_FALSE (state == SC_FAILED)) + { ret = false; goto out; - default: - break; } + packet->header.type = MESSAGE_DATA; if (!wg_create_buffer (vm, peer, (u8 *) packet, size_of_packet, &bi0)) @@ -166,22 +195,19 @@ wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer) } ip46_enqueue_packet (vm, bi0, false); - wg_timers_any_authenticated_packet_traversal (peer); + wg_timers_any_authenticated_packet_sent (peer); + wg_timers_any_authenticated_packet_traversal (peer); out: - clib_mem_free (packet); return ret; } bool wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer) { - wg_main_t *wmp = &wg_main; message_handshake_response_t packet; - peer->last_sent_handshake = vlib_time_now (vm); - if (noise_create_response (vm, &peer->remote, &packet.sender_index, @@ -189,17 +215,16 @@ wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer) packet.unencrypted_ephemeral, packet.encrypted_nothing)) { - f64 now = vlib_time_now (vm); packet.header.type = MESSAGE_HANDSHAKE_RESPONSE; cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet, sizeof (packet)); - if (noise_remote_begin_session (wmp->vlib_main, &peer->remote)) + if (noise_remote_begin_session (vm, &peer->remote)) { wg_timers_session_derived (peer); - wg_timers_any_authenticated_packet_traversal (peer); wg_timers_any_authenticated_packet_sent (peer); - peer->last_sent_handshake = now; + wg_timers_any_authenticated_packet_traversal (peer); + peer->last_sent_handshake = vlib_time_now (vm); u32 bi0 = 0; if (!wg_create_buffer (vm, peer, (u8 *) & packet, diff --git a/src/plugins/wireguard/wireguard_send.h b/src/plugins/wireguard/wireguard_send.h index 8f5e7ab87652..efe419494282 100755 --- a/src/plugins/wireguard/wireguard_send.h +++ b/src/plugins/wireguard/wireguard_send.h @@ -20,14 +20,15 @@ bool wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer); bool wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry); +void wg_send_handshake_from_mt (u32 peer_index, bool is_retry); bool wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer); always_inline void ip4_header_set_len_w_chksum (ip4_header_t * ip4, u16 len) { ip_csum_t sum = ip4->checksum; - u8 old = ip4->length; - u8 new = len; + u16 old = ip4->length; + u16 new = len; sum = ip_csum_update (sum, old, new, ip4_header_t, length); ip4->checksum = ip_csum_fold (sum); diff --git a/src/plugins/wireguard/wireguard_timer.c b/src/plugins/wireguard/wireguard_timer.c index e4d4030bb185..b7fd6891d149 100755 --- a/src/plugins/wireguard/wireguard_timer.c +++ b/src/plugins/wireguard/wireguard_timer.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include #include #include @@ -30,31 +31,77 @@ stop_timer (wg_peer_t * peer, u32 timer_id) { if (peer->timers[timer_id] != ~0) { - tw_timer_stop_16t_2w_512sl (&peer->timer_wheel, peer->timers[timer_id]); + tw_timer_stop_16t_2w_512sl (peer->timer_wheel, peer->timers[timer_id]); peer->timers[timer_id] = ~0; } } static void -start_or_update_timer (wg_peer_t * peer, u32 timer_id, u32 interval) +start_timer (wg_peer_t * peer, u32 timer_id, u32 interval_ticks) { + ASSERT (vlib_get_thread_index () == 0); + if (peer->timers[timer_id] == ~0) { - wg_main_t *wmp = &wg_main; peer->timers[timer_id] = - tw_timer_start_16t_2w_512sl (&peer->timer_wheel, peer - wmp->peers, - timer_id, interval); - } - else - { - tw_timer_update_16t_2w_512sl (&peer->timer_wheel, - peer->timers[timer_id], interval); + tw_timer_start_16t_2w_512sl (peer->timer_wheel, peer - wg_peer_pool, + timer_id, interval_ticks); } } +typedef struct +{ + u32 peer_idx; + u32 timer_id; + u32 interval_ticks; + +} wg_timers_args; + +static void * +start_timer_thread_fn (void *arg) +{ + wg_timers_args *a = arg; + wg_peer_t *peer = wg_peer_get (a->peer_idx); + + start_timer (peer, a->timer_id, a->interval_ticks); + return 0; +} + +static void +start_timer_from_mt (u32 peer_idx, u32 timer_id, u32 interval_ticks) +{ + wg_timers_args a = { + .peer_idx = peer_idx, + .timer_id = timer_id, + .interval_ticks = interval_ticks, + }; + + vl_api_rpc_call_main_thread (start_timer_thread_fn, (u8 *) & a, sizeof (a)); +} + +static inline u32 +timer_ticks_left (vlib_main_t * vm, f64 init_time_sec, u32 interval_ticks) +{ + static const int32_t rounding = (int32_t) (WHZ / 2); + int32_t ticks_remain; + + ticks_remain = (init_time_sec - vlib_time_now (vm)) * WHZ + interval_ticks; + return (ticks_remain > rounding) ? (u32) ticks_remain : 0; +} + static void wg_expired_retransmit_handshake (vlib_main_t * vm, wg_peer_t * peer) { + if (peer->rehandshake_started == ~0) + return; + + u32 ticks = timer_ticks_left (vm, peer->rehandshake_started, + peer->rehandshake_interval_tick); + if (ticks) + { + start_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE, ticks); + return; + } if (peer->timer_handshake_attempts > MAX_TIMER_HANDSHAKES) { @@ -63,17 +110,8 @@ wg_expired_retransmit_handshake (vlib_main_t * vm, wg_peer_t * peer) /* We set a timer for destroying any residue that might be left * of a partial exchange. */ + start_timer (peer, WG_TIMER_KEY_ZEROING, REJECT_AFTER_TIME * 3 * WHZ); - if (peer->timers[WG_TIMER_KEY_ZEROING] == ~0) - { - wg_main_t *wmp = &wg_main; - - peer->timers[WG_TIMER_KEY_ZEROING] = - tw_timer_start_16t_2w_512sl (&peer->timer_wheel, - peer - wmp->peers, - WG_TIMER_KEY_ZEROING, - REJECT_AFTER_TIME * 3 * WHZ); - } } else { @@ -85,13 +123,23 @@ wg_expired_retransmit_handshake (vlib_main_t * vm, wg_peer_t * peer) static void wg_expired_send_keepalive (vlib_main_t * vm, wg_peer_t * peer) { - wg_send_keepalive (vm, peer); - - if (peer->timer_need_another_keepalive) + if (peer->last_sent_packet < peer->last_received_packet) { - peer->timer_need_another_keepalive = false; - start_or_update_timer (peer, WG_TIMER_SEND_KEEPALIVE, - KEEPALIVE_TIMEOUT * WHZ); + u32 ticks = timer_ticks_left (vm, peer->last_received_packet, + KEEPALIVE_TIMEOUT * WHZ); + if (ticks) + { + start_timer (peer, WG_TIMER_SEND_KEEPALIVE, ticks); + return; + } + + wg_send_keepalive (vm, peer); + if (peer->timer_need_another_keepalive) + { + peer->timer_need_another_keepalive = false; + start_timer (peer, WG_TIMER_SEND_KEEPALIVE, + KEEPALIVE_TIMEOUT * WHZ); + } } } @@ -100,6 +148,18 @@ wg_expired_send_persistent_keepalive (vlib_main_t * vm, wg_peer_t * peer) { if (peer->persistent_keepalive_interval) { + f64 latest_time = peer->last_sent_packet > peer->last_received_packet + ? peer->last_sent_packet : peer->last_received_packet; + + u32 ticks = timer_ticks_left (vm, latest_time, + peer->persistent_keepalive_interval * + WHZ); + if (ticks) + { + start_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE, ticks); + return; + } + wg_send_keepalive (vm, peer); } } @@ -107,64 +167,81 @@ wg_expired_send_persistent_keepalive (vlib_main_t * vm, wg_peer_t * peer) static void wg_expired_new_handshake (vlib_main_t * vm, wg_peer_t * peer) { + u32 ticks = timer_ticks_left (vm, peer->last_sent_packet, + peer->new_handshake_interval_tick); + if (ticks) + { + start_timer (peer, WG_TIMER_NEW_HANDSHAKE, ticks); + return; + } + wg_send_handshake (vm, peer, false); } static void wg_expired_zero_key_material (vlib_main_t * vm, wg_peer_t * peer) { + u32 ticks = + timer_ticks_left (vm, peer->session_derived, REJECT_AFTER_TIME * 3 * WHZ); + if (ticks) + { + start_timer (peer, WG_TIMER_KEY_ZEROING, ticks); + return; + } + if (!peer->is_dead) { noise_remote_clear (vm, &peer->remote); } } - void wg_timers_any_authenticated_packet_traversal (wg_peer_t * peer) { if (peer->persistent_keepalive_interval) { - start_or_update_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE, - peer->persistent_keepalive_interval * WHZ); + start_timer_from_mt (peer - wg_peer_pool, + WG_TIMER_PERSISTENT_KEEPALIVE, + peer->persistent_keepalive_interval * WHZ); } } void wg_timers_any_authenticated_packet_sent (wg_peer_t * peer) { - stop_timer (peer, WG_TIMER_SEND_KEEPALIVE); + peer->last_sent_packet = vlib_time_now (vlib_get_main ()); } void wg_timers_handshake_initiated (wg_peer_t * peer) { - u32 interval = + peer->rehandshake_started = vlib_time_now (vlib_get_main ()); + peer->rehandshake_interval_tick = REKEY_TIMEOUT * WHZ + get_random_u32_max (REKEY_TIMEOUT_JITTER); - start_or_update_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE, interval); + + start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_RETRANSMIT_HANDSHAKE, + peer->rehandshake_interval_tick); } void wg_timers_session_derived (wg_peer_t * peer) { - start_or_update_timer (peer, WG_TIMER_KEY_ZEROING, - REJECT_AFTER_TIME * 3 * WHZ); + peer->session_derived = vlib_time_now (vlib_get_main ()); + + start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_KEY_ZEROING, + REJECT_AFTER_TIME * 3 * WHZ); } /* Should be called after an authenticated data packet is sent. */ void wg_timers_data_sent (wg_peer_t * peer) { - u32 interval = (KEEPALIVE_TIMEOUT + REKEY_TIMEOUT) * WHZ + + peer->new_handshake_interval_tick = + (KEEPALIVE_TIMEOUT + REKEY_TIMEOUT) * WHZ + get_random_u32_max (REKEY_TIMEOUT_JITTER); - if (peer->timers[WG_TIMER_NEW_HANDSHAKE] == ~0) - { - wg_main_t *wmp = &wg_main; - peer->timers[WG_TIMER_NEW_HANDSHAKE] = - tw_timer_start_16t_2w_512sl (&peer->timer_wheel, peer - wmp->peers, - WG_TIMER_NEW_HANDSHAKE, interval); - } + start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_NEW_HANDSHAKE, + peer->new_handshake_interval_tick); } /* Should be called after an authenticated data packet is received. */ @@ -173,16 +250,11 @@ wg_timers_data_received (wg_peer_t * peer) { if (peer->timers[WG_TIMER_SEND_KEEPALIVE] == ~0) { - wg_main_t *wmp = &wg_main; - peer->timers[WG_TIMER_SEND_KEEPALIVE] = - tw_timer_start_16t_2w_512sl (&peer->timer_wheel, peer - wmp->peers, - WG_TIMER_SEND_KEEPALIVE, - KEEPALIVE_TIMEOUT * WHZ); + start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_SEND_KEEPALIVE, + KEEPALIVE_TIMEOUT * WHZ); } else - { - peer->timer_need_another_keepalive = true; - } + peer->timer_need_another_keepalive = true; } /* Should be called after a handshake response message is received and processed @@ -191,15 +263,14 @@ wg_timers_data_received (wg_peer_t * peer) void wg_timers_handshake_complete (wg_peer_t * peer) { - stop_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE); - + peer->rehandshake_started = ~0; peer->timer_handshake_attempts = 0; } void wg_timers_any_authenticated_packet_received (wg_peer_t * peer) { - stop_timer (peer, WG_TIMER_NEW_HANDSHAKE); + peer->last_received_packet = vlib_time_now (vlib_get_main ()); } static vlib_node_registration_t wg_timer_mngr_node; @@ -222,7 +293,7 @@ expired_timer_callback (u32 * expired_timers) pool_index = expired_timers[i] & 0x0FFFFFFF; timer_id = expired_timers[i] >> 28; - peer = pool_elt_at_index (wmp->peers, pool_index); + peer = wg_peer_get (pool_index); peer->timers[timer_id] = ~0; } @@ -231,7 +302,7 @@ expired_timer_callback (u32 * expired_timers) pool_index = expired_timers[i] & 0x0FFFFFFF; timer_id = expired_timers[i] >> 28; - peer = pool_elt_at_index (wmp->peers, pool_index); + peer = wg_peer_get (pool_index); switch (timer_id) { case WG_TIMER_RETRANSMIT_HANDSHAKE: @@ -256,18 +327,14 @@ expired_timer_callback (u32 * expired_timers) } void -wg_timers_init (wg_peer_t * peer, f64 now) +wg_timer_wheel_init () { - for (int i = 0; i < WG_N_TIMERS; i++) - { - peer->timers[i] = ~0; - } - tw_timer_wheel_16t_2w_512sl_t *tw = &peer->timer_wheel; + wg_main_t *wmp = &wg_main; + tw_timer_wheel_16t_2w_512sl_t *tw = &wmp->timer_wheel; tw_timer_wheel_init_16t_2w_512sl (tw, expired_timer_callback, WG_TICK /* timer period in s */ , ~0); - tw->last_run_time = now; - peer->adj_index = INDEX_INVALID; + tw->last_run_time = vlib_time_now (wmp->vlib_main); } static uword @@ -275,22 +342,13 @@ wg_timer_mngr_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { wg_main_t *wmp = &wg_main; - wg_peer_t *peers; - wg_peer_t *peer; - while (1) { vlib_process_wait_for_event_or_clock (vm, WG_TICK); vlib_process_get_events (vm, NULL); - peers = wmp->peers; - /* *INDENT-OFF* */ - pool_foreach (peer, peers, - ({ - tw_timer_expire_timers_16t_2w_512sl - (&peer->timer_wheel, vlib_time_now (vm)); - })); - /* *INDENT-ON* */ + tw_timer_expire_timers_16t_2w_512sl (&wmp->timer_wheel, + vlib_time_now (vm)); } return 0; @@ -299,11 +357,15 @@ wg_timer_mngr_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, void wg_timers_stop (wg_peer_t * peer) { - stop_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE); - stop_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE); - stop_timer (peer, WG_TIMER_SEND_KEEPALIVE); - stop_timer (peer, WG_TIMER_NEW_HANDSHAKE); - stop_timer (peer, WG_TIMER_KEY_ZEROING); + ASSERT (vlib_get_thread_index () == 0); + if (peer->timer_wheel) + { + stop_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE); + stop_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE); + stop_timer (peer, WG_TIMER_SEND_KEEPALIVE); + stop_timer (peer, WG_TIMER_NEW_HANDSHAKE); + stop_timer (peer, WG_TIMER_KEY_ZEROING); + } } /* *INDENT-OFF* */ diff --git a/src/plugins/wireguard/wireguard_timer.h b/src/plugins/wireguard/wireguard_timer.h index 457dce286744..2cc5dd012844 100755 --- a/src/plugins/wireguard/wireguard_timer.h +++ b/src/plugins/wireguard/wireguard_timer.h @@ -38,7 +38,7 @@ typedef enum _wg_timers typedef struct wg_peer wg_peer_t; -void wg_timers_init (wg_peer_t * peer, f64 now); +void wg_timer_wheel_init (); void wg_timers_stop (wg_peer_t * peer); void wg_timers_data_sent (wg_peer_t * peer); void wg_timers_data_received (wg_peer_t * peer); diff --git a/src/scripts/fts.py b/src/scripts/fts.py index 249970405c40..b579190e433e 100755 --- a/src/scripts/fts.py +++ b/src/scripts/fts.py @@ -99,10 +99,10 @@ def print_maintainer(self, o): write = self.stream.write if type(o) is list: write('Maintainers: ' + - ', '.join(f'{m}' for m in + ', '.join('{m}'.format(m=m) for m in o) + ' \n') else: - write(f'Maintainer: {o} \n') + write('Maintainer: {o} \n'.format(o=o)) _dispatch['maintainer'] = print_maintainer @@ -112,39 +112,39 @@ def print_features(self, o, indent=0): indentstr = ' ' * indent if type(f) is dict: for k, v in f.items(): - write(f'{indentstr}- {k}\n') + write('{indentstr}- {k}\n'.format(indentstr=indentstr, k=k)) self.print_features(v, indent + 2) else: - write(f'{indentstr}- {f}\n') + write('{indentstr}- {f}\n'.format(indentstr=indentstr, f=f)) write('\n') _dispatch['features'] = print_features def print_markdown_header(self, o): write = self.stream.write - write(f'## {o}\n') + write('## {o}\n'.format(o=o)) version = version_from_git() - write(f'VPP version: {version}\n\n') + write('VPP version: {version}\n\n'.format(version=version)) _dispatch['markdown_header'] = print_markdown_header def print_name(self, o): write = self.stream.write - write(f'### {o}\n') + write('### {o}\n'.format(o=o)) self.toc.append(o) _dispatch['name'] = print_name def print_description(self, o): write = self.stream.write - write(f'\n{o}\n\n') + write('\n{o}\n\n'.format(o=o)) _dispatch['description'] = print_description def print_state(self, o): write = self.stream.write - write(f'Feature maturity level: {o} \n') + write('Feature maturity level: {o} \n'.format(o=o)) _dispatch['state'] = print_state def print_properties(self, o): write = self.stream.write - write(f'Supports: {" ".join(o)} \n') + write('Supports: {s} \n'.format(s=" ".join(o))) _dispatch['properties'] = print_properties def print_missing(self, o): @@ -155,7 +155,7 @@ def print_missing(self, o): def print_code(self, o): write = self.stream.write - write(f'Source Code: [{o}]({o}) \n') + write('Source Code: [{o}]({o}) \n'.format(o=o)) _dispatch['code'] = print_code def print(self, t, o): @@ -171,7 +171,7 @@ def output_toc(toc, stream): for t in toc: ref = t.lower().replace(' ', '-') - write(f'[{t}](#{ref}) \n') + write('[{t}](#{ref}) \n'.format(t=t, ref=ref)) def featuresort(k): return k[1]['name'] @@ -194,7 +194,8 @@ def output_markdown(features, fields, notfields): m = MarkDown(stream) m.print('markdown_header', 'Feature Details:') for path, featuredef in sorted(features.items(), key=featuresort): - codeurl = 'https://git.fd.io/vpp/tree/src/' + '/'.join(os.path.normpath(path).split('/')[1:-1]) + codeurl = 'https://git.fd.io/vpp/tree/src/' + \ + '/'.join(os.path.normpath(path).split('/')[1:-1]) featuredef['code'] = codeurl for k, v in sorted(featuredef.items(), key=featurelistsort): if notfields: @@ -253,8 +254,8 @@ def main(): try: validate(instance=cfg, schema=schema) except exceptions.ValidationError: - print(f'File does not validate: {featurefile}', - file=sys.stderr) + print('File does not validate: {featurefile}' \ + .format(featurefile=featurefile), file=sys.stderr) raise features[featurefile] = cfg diff --git a/src/scripts/version b/src/scripts/version index 7e7711637cb0..1c265bff68f9 100755 --- a/src/scripts/version +++ b/src/scripts/version @@ -30,13 +30,18 @@ TAG=$(echo ${vstring} | cut -d- -f1 | sed -e 's/^v//') ADD=$(echo ${vstring} | cut -s -d- -f2) POINT=$(echo ${TAG} | cut -d. -f3) +# during make pkg-rpm vstring ends up being vXX.YY, which is not what we expect. Fix it up. +if [ -z "${ADD}" ]; then + ADD="0" +fi + # if this is a "implicit zeroth" release (e.g. 19.08), check if we need to add ".0" # to fix the artifact versioning sorting if [ -z "${POINT}" ]; then # verify that we are not: # - directly on the XX.YY tag (then ADD will equal "0" by its construction) # - not on any of the builds past "-rc[123]" but before releases - then ADD will be "rc[123]" - if [ "${ADD}" != "0" -a "${ADD}" != "rc0" -a "${ADD}" != "rc1" -a "${ADD}" != "rc2" ]; then + if [ "${ADD}" != "0" -a "${ADD}" != "rc0" -a "${ADD}" != "rc1" -a "${ADD}" != "rc2" -a "${ADD}" != "rc3" -a "${ADD}" != "rc4" ]; then TAG="${TAG}.0" fi fi diff --git a/src/scripts/vnet/ipsec_spd_vrf b/src/scripts/vnet/ipsec_spd_vrf new file mode 100644 index 000000000000..faa891dc2c45 --- /dev/null +++ b/src/scripts/vnet/ipsec_spd_vrf @@ -0,0 +1,72 @@ + +create packet-generator interface pg0 +set int mac addr pg0 4.5.6 + +create sub-interfaces pg0 1 +create sub-interfaces pg0 2 +create sub-interfaces pg0 3 + +ip table add 1 +ip table add 2 +ip table add 3 + +set int ip table pg0.1 1 +set int ip table pg0.2 2 +set int ip table pg0.3 3 + +set int ip address pg0.1 192.168.0.1/24 +set int ip address pg0.2 192.168.0.1/24 +set int ip address pg0.3 192.168.0.1/24 + +set int state pg0 up +set int state pg0.1 up +set int state pg0.2 up +set int state pg0.3 up + +ipsec sa add 2 spi 2 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128 tunnel-src 192.168.0.1 tunnel-dst 192.168.0.2 tx-table-id 2 +ipsec sa add 3 spi 3 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128 tunnel-src 192.168.0.1 tunnel-dst 192.168.0.2 tx-table-id 3 + +ipsec spd add 1 +set interface ipsec spd pg0.1 1 +ipsec policy add spd 1 priority 100 outbound action bypass protocol 50 +ipsec policy add spd 1 priority 10 outbound action protect sa 2 local-ip-range 10.5.0.0 - 10.5.0.255 remote-ip-range 10.6.0.0 - 10.6.0.16 +ipsec policy add spd 1 priority 10 outbound action protect sa 3 local-ip-range 10.5.0.0 - 10.5.0.255 remote-ip-range 10.6.0.17 - 10.6.0.32 + +ip route table 1 add 10.6.0.0/16 via 192.168.0.2 pg0.1 +set ip neighbor pg0.1 192.168.0.2 00:11:22:33:44:55 +set ip neighbor pg0.2 192.168.0.2 00:11:22:33:44:55 +set ip neighbor pg0.3 192.168.0.2 00:11:22:33:44:55 + +trace add pg-input 100 + +packet-generator new { + name ipsec2 + limit 1 + rate 1e4 + node ethernet-input + interface pg0 + size 100-100 + data { + IP4: 1.2.3 -> 4.5.6 vlan 1 + UDP: 10.5.0.1 -> 10.6.0.1 + UDP: 4321 -> 1234 + length 72 + incrementing 100 + } +} + +packet-generator new { + name ipsec3 + limit 1 + rate 1e4 + node ethernet-input + interface pg0 + size 100-100 + data { + IP4: 1.2.3 -> 4.5.6 vlan 1 + UDP: 10.5.0.1 -> 10.6.0.22 + UDP: 4321 -> 1234 + length 72 + incrementing 100 + } +} diff --git a/src/scripts/vnet/ipsec_tun_protect b/src/scripts/vnet/ipsec_tun_protect index b30281fcf924..6dc4dd7b7808 100644 --- a/src/scripts/vnet/ipsec_tun_protect +++ b/src/scripts/vnet/ipsec_tun_protect @@ -22,7 +22,7 @@ ipsec sa add 20 spi 200 crypto-key 6541686776336961656264656f6f6579 crypto-alg a ipsec sa add 30 spi 300 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128 create ipip tunnel src 10.0.0.1 dst 10.0.0.2 -create ipip tunnel src 10.0.0.2 dst 10.0.0.1 +create ipip tunnel src 10.0.0.2 dst 10.0.0.1 outer-table-id 1 ipsec tunnel protect ipip0 sa-in 20 sa-out 30 ipsec tunnel protect ipip1 sa-in 30 sa-out 20 diff --git a/src/svm/fifo_segment.c b/src/svm/fifo_segment.c index 3fd7d9d6ede7..80a22b199787 100644 --- a/src/svm/fifo_segment.c +++ b/src/svm/fifo_segment.c @@ -784,6 +784,7 @@ fsh_slice_collect_chunks (fifo_segment_header_t * fsh, while (c) { + CLIB_MEM_UNPOISON (c, sizeof (*c)); next = c->next; fl_index = fs_freelist_for_size (c->length); c->next = fss->free_chunks[fl_index]; diff --git a/src/svm/svm_fifo.c b/src/svm/svm_fifo.c index fda9481e7216..22f92ba36d69 100644 --- a/src/svm/svm_fifo.c +++ b/src/svm/svm_fifo.c @@ -70,6 +70,8 @@ CLIB_MARCH_FN (svm_fifo_copy_from_chunk, void, svm_fifo_t * f, c = c->next; while ((to_copy -= n_chunk)) { + CLIB_MEM_UNPOISON (c, sizeof (*c)); + CLIB_MEM_UNPOISON (c->data, c->length); n_chunk = clib_min (c->length, to_copy); clib_memcpy_fast (dst + (len - to_copy), &c->data[0], n_chunk); c = c->length <= to_copy ? c->next : c; @@ -1054,6 +1056,7 @@ svm_fifo_peek (svm_fifo_t * f, u32 offset, u32 len, u8 * dst) len = clib_min (cursize - offset, len); head_idx = head + offset; + CLIB_MEM_UNPOISON (f->ooo_deq, sizeof (*f->ooo_deq)); if (!f->ooo_deq || !f_chunk_includes_pos (f->ooo_deq, head_idx)) f_update_ooo_deq (f, head_idx, head_idx + len); diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index 9d74a5b0357f..bfc77ca7019d 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -11,7 +11,7 @@ import sys from subprocess import Popen, PIPE -assert sys.version_info >= (3, 6), \ +assert sys.version_info >= (3, 5), \ "Not supported Python version: {}".format(sys.version) log = logging.getLogger('vppapigen') @@ -780,12 +780,13 @@ def parse_fd(self, fd, debug=0): def parse_filename(self, filename, debug=0): if self.revision: - git_show = f'git show {self.revision}:{filename}' + git_show = 'git show {}:{}'.format(self.revision, filename) proc = Popen(git_show.split(), stdout=PIPE, encoding='utf-8') try: data, errs = proc.communicate() if proc.returncode != 0: - print(f'File not found: {self.revision}:{filename}', file=sys.stderr) + print('File not found: {}:{}'.format(self.revision, + filename), file=sys.stderr) sys.exit(2) return self.parse_string(data, debug=debug) except Exception as e: @@ -795,7 +796,7 @@ def parse_filename(self, filename, debug=0): with open(filename, encoding='utf-8') as fd: return self.parse_fd(fd, None) except FileNotFoundError: - print(f'File not found: {filename}', file=sys.stderr) + print('File not found: {}'.format(filename), file=sys.stderr) sys.exit(2) def autoreply_block(self, name, parent): diff --git a/src/vat/api_format.c b/src/vat/api_format.c index f1935dee33a5..e3a2fe734bef 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -14305,7 +14305,7 @@ unformat_lisp_eid_vat (unformat_input_t * input, va_list * args) { a->type = 0; /* ip prefix type */ } - else if (unformat (input, "%U", unformat_ethernet_address, a->addr.mac)) + else if (unformat (input, "%U", unformat_ethernet_address, &a->addr.mac)) { a->type = 1; /* mac type */ } @@ -21031,7 +21031,7 @@ _(one_eid_table_dump, "[eid / | ] [vni] " \ _(one_add_del_ndp_entry, "[del] mac bd ip6 ") \ _(one_ndp_bd_get, "") \ _(one_ndp_entries_get, "bd ") \ -_(one_add_del_l2_arp_entry, "[del] mac bd ip4 ") \ +_(one_add_del_l2_arp_entry, "[del] mac bd ip ") \ _(one_l2_arp_bd_get, "") \ _(one_l2_arp_entries_get, "bd ") \ _(one_stats_enable_disable, "enable|disable") \ diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c index 9174e5d6d2f4..dd8ffb46e1bd 100644 --- a/src/vcl/vppcom.c +++ b/src/vcl/vppcom.c @@ -1892,7 +1892,7 @@ vppcom_session_read_internal (uint32_t session_handle, void *buf, int n, u8 peek) { vcl_worker_t *wrk = vcl_worker_get_current (); - int n_read = 0, is_nonblocking; + int rv, n_read = 0, is_nonblocking; vcl_session_t *s = 0; svm_fifo_t *rx_fifo; svm_msg_q_msg_t msg; @@ -1949,10 +1949,15 @@ vppcom_session_read_internal (uint32_t session_handle, void *buf, int n, } } +read_again: + if (s->is_dgram) - n_read = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek); + rv = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek); else - n_read = app_recv_stream_raw (rx_fifo, buf, n, 0, peek); + rv = app_recv_stream_raw (rx_fifo, buf, n, 0, peek); + + ASSERT (rv >= 0); + n_read += rv; if (svm_fifo_is_empty_cons (rx_fifo)) { @@ -1960,12 +1965,19 @@ vppcom_session_read_internal (uint32_t session_handle, void *buf, int n, if (!svm_fifo_is_empty_cons (rx_fifo) && svm_fifo_set_event (s->rx_fifo) && is_nonblocking) { - session_event_t *e; vec_add2 (wrk->unhandled_evts_vector, e, 1); e->event_type = SESSION_IO_EVT_RX; e->session_index = s->session_index; } } + else if (PREDICT_FALSE (rv < n)) + { + /* More data enqueued while reading. Try to drain it + * or fill the buffer */ + buf += rv; + n -= rv; + goto read_again; + } if (PREDICT_FALSE (svm_fifo_needs_deq_ntf (rx_fifo, n_read))) { diff --git a/src/vlib/linux/pci.c b/src/vlib/linux/pci.c index 168acde72886..996c312b1704 100644 --- a/src/vlib/linux/pci.c +++ b/src/vlib/linux/pci.c @@ -1123,9 +1123,21 @@ vlib_pci_map_region_int (vlib_main_t * vm, vlib_pci_dev_handle_t h, clib_error_t *error; int flags = MAP_SHARED; u64 size = 0, offset = 0; + u16 command; pci_log_debug (vm, p, "map region %u to va %p", bar, addr); + if ((error = vlib_pci_read_config_u16 (vm, h, 4, &command))) + return error; + + if (!(command & PCI_COMMAND_MEMORY)) + { + pci_log_debug (vm, p, "setting memory enable bit"); + command |= PCI_COMMAND_MEMORY; + if ((error = vlib_pci_write_config_u16 (vm, h, 4, &command))) + return error; + } + if ((error = vlib_pci_region (vm, h, bar, &fd, &size, &offset))) return error; diff --git a/src/vlib/main.c b/src/vlib/main.c index bada9bcfdff8..bfe97953a031 100644 --- a/src/vlib/main.c +++ b/src/vlib/main.c @@ -1771,28 +1771,26 @@ vlib_main_or_worker_loop (vlib_main_t * vm, int is_main) } if (!is_main) + vlib_worker_thread_barrier_check (); + + if (PREDICT_FALSE (vm->check_frame_queues + frame_queue_check_counter)) { - vlib_worker_thread_barrier_check (); - if (PREDICT_FALSE (vm->check_frame_queues + - frame_queue_check_counter)) - { - u32 processed = 0; + u32 processed = 0; - if (vm->check_frame_queues) - { - frame_queue_check_counter = 100; - vm->check_frame_queues = 0; - } + if (vm->check_frame_queues) + { + frame_queue_check_counter = 100; + vm->check_frame_queues = 0; + } - vec_foreach (fqm, tm->frame_queue_mains) - processed += vlib_frame_queue_dequeue (vm, fqm); + vec_foreach (fqm, tm->frame_queue_mains) + processed += vlib_frame_queue_dequeue (vm, fqm); - /* No handoff queue work found? */ - if (processed) - frame_queue_check_counter = 100; - else - frame_queue_check_counter--; - } + /* No handoff queue work found? */ + if (processed) + frame_queue_check_counter = 100; + else + frame_queue_check_counter--; } if (PREDICT_FALSE (vec_len (vm->worker_thread_main_loop_callbacks))) diff --git a/src/vlib/node.c b/src/vlib/node.c index 119b4718090f..169c67b0888e 100644 --- a/src/vlib/node.c +++ b/src/vlib/node.c @@ -347,7 +347,10 @@ register_node (vlib_main_t * vm, vlib_node_registration_t * r) /* Node names must be unique. */ { - vlib_node_t *o = vlib_get_node_by_name (vm, n->name); + /* vlib_get_node_by_name() expects NULL-terminated strings */ + u8 *name = format (0, "%v%c", n->name, 0); + vlib_node_t *o = vlib_get_node_by_name (vm, name); + vec_free (name); if (o) clib_error ("more than one node named `%v'", n->name); } diff --git a/src/vlib/node_funcs.h b/src/vlib/node_funcs.h index dfeba17ab09b..33bdc795816a 100644 --- a/src/vlib/node_funcs.h +++ b/src/vlib/node_funcs.h @@ -455,6 +455,13 @@ vlib_current_process (vlib_main_t * vm) return vlib_get_current_process (vm)->node_runtime.node_index; } +always_inline u32 +vlib_get_current_process_node_index (vlib_main_t * vm) +{ + vlib_process_t *process = vlib_get_current_process (vm); + return process->node_runtime.node_index; +} + /** Returns TRUE if a process suspend time is less than 10us @param dt - remaining poll time in seconds @returns 1 if dt < 10e-6, 0 otherwise diff --git a/src/vlibmemory/socket_api.c b/src/vlibmemory/socket_api.c index a3a0c3b2eb9b..32434cf2b02f 100644 --- a/src/vlibmemory/socket_api.c +++ b/src/vlibmemory/socket_api.c @@ -645,6 +645,11 @@ vl_api_sock_init_shm_t_handler (vl_api_sock_init_shm_t * mp) if ((rv = ssvm_master_init_memfd (memfd))) goto reply; + /* delete the unused heap created in ssvm_server_init_memfd and mark it + * accessible again for ASAN */ + clib_mem_destroy_mspace (memfd->sh->heap); + CLIB_MEM_UNPOISON ((void *) memfd->sh->ssvm_va, memfd->ssvm_size); + /* Remember to close this fd when the socket connection goes away */ vec_add1 (regp->additional_fds_to_close, memfd->fd); diff --git a/src/vnet/arp/arp.c b/src/vnet/arp/arp.c index 14a1ae97d1ee..b8104bdfbaef 100644 --- a/src/vnet/arp/arp.c +++ b/src/vnet/arp/arp.c @@ -852,17 +852,8 @@ static clib_error_t * vnet_arp_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) { ethernet_arp_main_t *am = ðernet_arp_main; - - if (!is_add && sw_if_index != ~0) - { - arp_disable (am, sw_if_index); - } - else if (is_add) - { - vnet_feature_enable_disable ("arp", "arp-disabled", - sw_if_index, 1, NULL, 0); - } - + if (is_add) + arp_disable (am, sw_if_index); return (NULL); } diff --git a/src/vnet/bfd/bfd_main.c b/src/vnet/bfd/bfd_main.c index 4b8975732af2..807221637624 100644 --- a/src/vnet/bfd/bfd_main.c +++ b/src/vnet/bfd/bfd_main.c @@ -1190,6 +1190,7 @@ bfd_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) } else { + bfd_unlock (bm); (void) vlib_process_wait_for_event (vm); } event_type = vlib_process_get_events (vm, &event_data); diff --git a/src/vnet/config.c b/src/vnet/config.c index 4e9fa36dfc30..c9d4909cdebe 100644 --- a/src/vnet/config.c +++ b/src/vnet/config.c @@ -241,6 +241,14 @@ vnet_get_config_heap (vnet_config_main_t * cm, u32 ci) return heap_elt_at_index (cm->config_string_heap, ci); } +void +vnet_config_del (vnet_config_main_t * cm, u32 config_id) +{ + u32 *p = vnet_get_config_heap (cm, config_id); + vnet_config_t *old = pool_elt_at_index (cm->config_pool, p[-1]); + remove_reference (cm, old); +} + u32 vnet_config_modify_end_node (vlib_main_t * vm, vnet_config_main_t * cm, diff --git a/src/vnet/config.h b/src/vnet/config.h index 7eb3cf0ae1f8..ccbbbf433e2a 100644 --- a/src/vnet/config.h +++ b/src/vnet/config.h @@ -147,6 +147,8 @@ void vnet_config_init (vlib_main_t * vm, int n_start_node_names, char *feature_node_names[], int n_feature_node_names); +void vnet_config_del (vnet_config_main_t * cm, u32 config_id); + /* Calls to add/delete features from configurations. */ u32 vnet_config_add_feature (vlib_main_t * vm, vnet_config_main_t * cm, diff --git a/src/vnet/cop/cop.api b/src/vnet/cop/cop.api index 39677a266739..75fd15bbb9fb 100644 --- a/src/vnet/cop/cop.api +++ b/src/vnet/cop/cop.api @@ -26,7 +26,7 @@ import "vnet/interface_types.api"; autoreply define cop_interface_enable_disable { - option deprecated="v21.01"; + option deprecated; u32 client_index; u32 context; vl_api_interface_index_t sw_if_index; @@ -47,7 +47,7 @@ autoreply define cop_interface_enable_disable autoreply define cop_whitelist_enable_disable { - option deprecated="v21.01"; + option deprecated; u32 client_index; u32 context; vl_api_interface_index_t sw_if_index; diff --git a/src/vnet/crypto/crypto.api b/src/vnet/crypto/crypto.api index 04b0cfdeaa34..6eccd8524ba9 100644 --- a/src/vnet/crypto/crypto.api +++ b/src/vnet/crypto/crypto.api @@ -41,7 +41,7 @@ autoreply define crypto_set_async_dispatch vl_api_crypto_dispatch_mode_t mode; }; - /** \brief crypto: use polling or interrupt dispatch + /** \brief crypto: set crypto handler @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param alg_name - Name of the algorithm to add diff --git a/src/vnet/crypto/crypto.h b/src/vnet/crypto/crypto.h index 07a73f14d043..20457074eb23 100644 --- a/src/vnet/crypto/crypto.h +++ b/src/vnet/crypto/crypto.h @@ -306,7 +306,7 @@ typedef struct i16 integ_start_offset; u32 crypto_total_length; /* adj total_length for integ, e.g.4 bytes for IPSec ESN */ - u16 integ_length_adj; + i16 integ_length_adj; u8 *iv; union { @@ -599,7 +599,7 @@ vnet_crypto_async_add_to_frame (vlib_main_t * vm, u32 key_index, u32 crypto_len, i16 integ_len_adj, i16 crypto_start_offset, - u16 integ_start_offset, + i16 integ_start_offset, u32 buffer_index, u16 next_node, u8 * iv, u8 * tag, u8 * aad, u8 flags) diff --git a/src/vnet/devices/tap/cli.c b/src/vnet/devices/tap/cli.c index 7580d9200616..89b2ff091b09 100644 --- a/src/vnet/devices/tap/cli.c +++ b/src/vnet/devices/tap/cli.c @@ -142,7 +142,7 @@ VLIB_CLI_COMMAND (tap_create_command, static) = { "[host-ip4-addr ] [host-ip6-addr ] " "[host-ip4-gw ] [host-ip6-gw ] " "[host-mac-addr ] [host-if-name ] " - "[host-mtu-size ] [no-gso|gso|csum-offload|gro-coalesce] " + "[host-mtu-size ] [no-gso|gso [gro-coalesce]|csum-offload] " "[persist] [attach] [tun] [packed] [in-order]", .function = tap_create_command_fn, }; diff --git a/src/vnet/devices/tap/tap.c b/src/vnet/devices/tap/tap.c index c265aa201ec2..040ec1f1ea57 100644 --- a/src/vnet/devices/tap/tap.c +++ b/src/vnet/devices/tap/tap.c @@ -731,7 +731,6 @@ tap_create_if (vlib_main_t * vm, tap_create_if_args_t * args) if ((args->tap_flags & TAP_FLAG_GSO) && (args->tap_flags & TAP_FLAG_GRO_COALESCE)) { - vif->packet_coalesce = 1; virtio_set_packet_coalesce (vif); } vnet_hw_interface_set_input_node (vnm, vif->hw_if_index, @@ -900,7 +899,6 @@ tap_gso_enable_disable (vlib_main_t * vm, u32 sw_if_index, int enable_disable, } if (is_packet_coalesce) { - vif->packet_coalesce = 1; virtio_set_packet_coalesce (vif); } } @@ -946,8 +944,7 @@ tap_dump_ifs (tap_interface_details_t ** out_tapids) tapid->sw_if_index = vif->sw_if_index; hi = vnet_get_hw_interface (vnm, vif->hw_if_index); clib_memcpy(tapid->dev_name, hi->name, - MIN (ARRAY_LEN (tapid->dev_name) - 1, - strlen ((const char *) hi->name))); + MIN (ARRAY_LEN (tapid->dev_name) - 1, vec_len (hi->name))); vring = vec_elt_at_index (vif->rxq_vrings, RX_QUEUE_ACCESS(0)); tapid->rx_ring_sz = vring->size; vring = vec_elt_at_index (vif->txq_vrings, TX_QUEUE_ACCESS(0)); diff --git a/src/vnet/devices/virtio/device.c b/src/vnet/devices/virtio/device.c index 56c0a98491e1..7fa4dde4ea32 100644 --- a/src/vnet/devices/virtio/device.c +++ b/src/vnet/devices/virtio/device.c @@ -719,24 +719,38 @@ virtio_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid, virtio_main_t *mm = &virtio_main; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance); - virtio_vring_t *vring = vec_elt_at_index (vif->rxq_vrings, qid); + virtio_vring_t *rx_vring = vec_elt_at_index (vif->rxq_vrings, qid); + virtio_vring_t *tx_vring = 0; if (vif->type == VIRTIO_IF_TYPE_PCI && !(vif->support_int_mode)) { - vring->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; + rx_vring->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; return clib_error_return (0, "interrupt mode is not supported"); } if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) { - /* only enable packet coalesce in poll mode */ - gro_flow_table_set_is_enable (vring->flow_table, 1); - vring->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; + vec_foreach (tx_vring, vif->txq_vrings) + { + /* only enable packet coalesce in poll mode */ + gro_flow_table_set_is_enable (tx_vring->flow_table, 1); + } + rx_vring->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; } else { - gro_flow_table_set_is_enable (vring->flow_table, 0); - vring->avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; + if (vif->packet_coalesce) + { + virtio_log_warning (vif, + "interface %U is in interrupt mode, disabling packet coalescing", + format_vnet_sw_if_index_name, vnet_get_main (), + vif->sw_if_index); + vec_foreach (tx_vring, vif->txq_vrings) + { + gro_flow_table_set_is_enable (tx_vring->flow_table, 0); + } + } + rx_vring->avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; } return 0; diff --git a/src/vnet/devices/virtio/node.c b/src/vnet/devices/virtio/node.c index 7fabe36401cf..1c9cfd0cc03f 100644 --- a/src/vnet/devices/virtio/node.c +++ b/src/vnet/devices/virtio/node.c @@ -279,10 +279,12 @@ virtio_device_input_gso_inline (vlib_main_t * vm, vlib_node_runtime_t * node, u16 last = vring->last_used_idx; u16 n_left = vring->used->idx - last; - if (vif->packet_coalesce) + if (vif->packet_coalesce + && clib_spinlock_trylock_if_init (&txq_vring->lockp)) { vnet_gro_flow_table_schedule_node_on_dispatcher (vm, txq_vring->flow_table); + clib_spinlock_unlock_if_init (&txq_vring->lockp); } if ((vring->used->flags & VRING_USED_F_NO_NOTIFY) == 0 && diff --git a/src/vnet/devices/virtio/pci.c b/src/vnet/devices/virtio/pci.c index 5ba9f36de81d..7b7a12028333 100644 --- a/src/vnet/devices/virtio/pci.c +++ b/src/vnet/devices/virtio/pci.c @@ -486,7 +486,11 @@ virtio_pci_control_vring_init (vlib_main_t * vm, virtio_if_t * vif, virtio_log_debug (vif, "control-queue: number %u, size %u", queue_num, queue_size); vif->virtio_pci_func->setup_queue (vm, vif, queue_num, ptr); - vring->kick_fd = -1; + vring->queue_notify_offset = + vif->notify_off_multiplier * + vif->virtio_pci_func->get_queue_notify_off (vm, vif, queue_num); + virtio_log_debug (vif, "queue-notify-offset: number %u, offset %u", + queue_num, vring->queue_notify_offset); return error; } @@ -494,6 +498,7 @@ virtio_pci_control_vring_init (vlib_main_t * vm, virtio_if_t * vif, clib_error_t * virtio_pci_vring_init (vlib_main_t * vm, virtio_if_t * vif, u16 queue_num) { + vlib_thread_main_t *vtm = vlib_get_thread_main (); clib_error_t *error = 0; u16 queue_size = 0; virtio_vring_t *vring; @@ -519,7 +524,8 @@ virtio_pci_vring_init (vlib_main_t * vm, virtio_if_t * vif, u16 queue_num) vec_validate_aligned (vif->txq_vrings, TX_QUEUE_ACCESS (queue_num), CLIB_CACHE_LINE_BYTES); vring = vec_elt_at_index (vif->txq_vrings, TX_QUEUE_ACCESS (queue_num)); - clib_spinlock_init (&vring->lockp); + if (vif->max_queue_pairs < vtm->n_vlib_mains) + clib_spinlock_init (&vring->lockp); } else { @@ -560,7 +566,11 @@ virtio_pci_vring_init (vlib_main_t * vm, virtio_if_t * vif, u16 queue_num) if (vif->virtio_pci_func->setup_queue (vm, vif, queue_num, ptr)) return clib_error_return (0, "error in queue address setup"); - vring->kick_fd = -1; + vring->queue_notify_offset = + vif->notify_off_multiplier * + vif->virtio_pci_func->get_queue_notify_off (vm, vif, queue_num); + virtio_log_debug (vif, "queue-notify-offset: number %u, offset %u", + queue_num, vring->queue_notify_offset); return error; } @@ -773,6 +783,7 @@ virtio_pci_read_caps (vlib_main_t * vm, virtio_if_t * vif, void **bar) if (common_cfg == 0 || notify == 0 || dev_cfg == 0 || isr == 0) { vif->virtio_pci_func = &virtio_pci_legacy_func; + vif->notify_off_multiplier = 0; virtio_log_debug (vif, "legacy virtio pci device found"); return error; } @@ -781,9 +792,14 @@ virtio_pci_read_caps (vlib_main_t * vm, virtio_if_t * vif, void **bar) vif->virtio_pci_func = &virtio_pci_modern_func; if (!pci_cfg) - virtio_log_debug (vif, "modern virtio pci device found"); + { + virtio_log_debug (vif, "modern virtio pci device found"); + } + else + { + virtio_log_debug (vif, "transitional virtio pci device found"); + } - virtio_log_debug (vif, "transitional virtio pci device found"); return error; } @@ -1229,8 +1245,6 @@ virtio_pci_delete_if (vlib_main_t * vm, virtio_if_t * vif) vec_foreach_index (i, vif->rxq_vrings) { virtio_vring_t *vring = vec_elt_at_index (vif->rxq_vrings, i); - if (vring->kick_fd != -1) - close (vring->kick_fd); if (vring->used) { virtio_free_rx_buffers (vm, vring); @@ -1242,8 +1256,6 @@ virtio_pci_delete_if (vlib_main_t * vm, virtio_if_t * vif) vec_foreach_index (i, vif->txq_vrings) { virtio_vring_t *vring = vec_elt_at_index (vif->txq_vrings, i); - if (vring->kick_fd != -1) - close (vring->kick_fd); if (vring->used) { virtio_free_used_desc (vm, vring); diff --git a/src/vnet/devices/virtio/pci.h b/src/vnet/devices/virtio/pci.h index ab5c6f15ec21..eeb0f9454bbb 100644 --- a/src/vnet/devices/virtio/pci.h +++ b/src/vnet/devices/virtio/pci.h @@ -195,7 +195,10 @@ typedef struct _virtio_pci_func u8 (*setup_queue) (vlib_main_t * vm, virtio_if_t * vif, u16 queue_id, void *p); void (*del_queue) (vlib_main_t * vm, virtio_if_t * vif, u16 queue_id); - void (*notify_queue) (vlib_main_t * vm, virtio_if_t * vif, u16 queue_id); + u16 (*get_queue_notify_off) (vlib_main_t * vm, virtio_if_t * vif, + u16 queue_id); + void (*notify_queue) (vlib_main_t * vm, virtio_if_t * vif, u16 queue_id, + u16 queue_notify_offset); u16 (*set_config_irq) (vlib_main_t * vm, virtio_if_t * vif, u16 vec); u16 (*set_queue_irq) (vlib_main_t * vm, virtio_if_t * vif, u16 vec, diff --git a/src/vnet/devices/virtio/vhost_user_input.c b/src/vnet/devices/virtio/vhost_user_input.c index ea8e7d6f777c..1209e13d742a 100644 --- a/src/vnet/devices/virtio/vhost_user_input.c +++ b/src/vnet/devices/virtio/vhost_user_input.c @@ -292,15 +292,11 @@ vhost_user_handle_rx_offload (vlib_buffer_t * b0, u8 * b0_data, tcp_header_t *tcp = (tcp_header_t *) (b0_data + vnet_buffer (b0)->l4_hdr_offset); l4_hdr_sz = tcp_header_bytes (tcp); - tcp->checksum = 0; b0->flags |= VNET_BUFFER_F_OFFLOAD_TCP_CKSUM; } else if (l4_proto == IP_PROTOCOL_UDP) { - udp_header_t *udp = - (udp_header_t *) (b0_data + vnet_buffer (b0)->l4_hdr_offset); - l4_hdr_sz = sizeof (*udp); - udp->checksum = 0; + l4_hdr_sz = sizeof (udp_header_t); b0->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; } diff --git a/src/vnet/devices/virtio/vhost_user_output.c b/src/vnet/devices/virtio/vhost_user_output.c index 2d17ddfda047..54d478c7034c 100644 --- a/src/vnet/devices/virtio/vhost_user_output.c +++ b/src/vnet/devices/virtio/vhost_user_output.c @@ -256,18 +256,12 @@ vhost_user_handle_tx_offload (vhost_user_intf_t * vui, vlib_buffer_t * b, hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = gho.l4_hdr_offset; hdr->csum_offset = offsetof (udp_header_t, checksum); - udp_header_t *udp = - (udp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); - udp->checksum = 0; } else if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM) { hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = gho.l4_hdr_offset; hdr->csum_offset = offsetof (tcp_header_t, checksum); - tcp_header_t *tcp = - (tcp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); - tcp->checksum = 0; } /* GSO offload */ diff --git a/src/vnet/devices/virtio/virtio.api b/src/vnet/devices/virtio/virtio.api index 143d25b73ab0..3c8aed2e1591 100644 --- a/src/vnet/devices/virtio/virtio.api +++ b/src/vnet/devices/virtio/virtio.api @@ -32,7 +32,7 @@ import "vlib/pci/pci_types.api"; */ define virtio_pci_create { - option deprecated="21.01"; + option deprecated; u32 client_index; u32 context; vl_api_pci_address_t pci_addr; @@ -50,7 +50,7 @@ define virtio_pci_create */ define virtio_pci_create_reply { - option deprecated="21.01"; + option deprecated; u32 context; i32 retval; vl_api_interface_index_t sw_if_index; diff --git a/src/vnet/devices/virtio/virtio.c b/src/vnet/devices/virtio/virtio.c index ec22a0d45f4d..e74f3783756d 100644 --- a/src/vnet/devices/virtio/virtio.c +++ b/src/vnet/devices/virtio/virtio.c @@ -226,6 +226,7 @@ virtio_set_packet_coalesce (virtio_if_t * vif) vnet_main_t *vnm = vnet_get_main (); vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vif->hw_if_index); virtio_vring_t *vring; + vif->packet_coalesce = 1; vec_foreach (vring, vif->txq_vrings) { gro_flow_table_init (&vring->flow_table, diff --git a/src/vnet/devices/virtio/virtio.h b/src/vnet/devices/virtio/virtio.h index f1eaa07fe5d7..309a2c37664f 100644 --- a/src/vnet/devices/virtio/virtio.h +++ b/src/vnet/devices/virtio/virtio.h @@ -65,8 +65,15 @@ typedef struct vring_avail_t *avail; u16 desc_in_use; u16 desc_next; - int kick_fd; - int call_fd; + union + { + struct + { + int kick_fd; + int call_fd; + }; + u16 queue_notify_offset; + }; u8 buffer_pool_index; u16 size; u16 queue_id; @@ -129,7 +136,15 @@ typedef struct /* error */ clib_error_t *error; - u8 mac_addr[6]; + union + { + struct + { + u32 mac_addr32; + u16 mac_addr16; + }; + u8 mac_addr[6]; + }; union { struct /* tun/tap interface */ @@ -196,9 +211,11 @@ extern void virtio_show (vlib_main_t * vm, u32 * hw_if_indices, u8 show_descr, u32 type); extern void virtio_set_packet_coalesce (virtio_if_t * vif); extern void virtio_pci_legacy_notify_queue (vlib_main_t * vm, - virtio_if_t * vif, u16 queue_id); + virtio_if_t * vif, u16 queue_id, + u16 queue_notify_offset); extern void virtio_pci_modern_notify_queue (vlib_main_t * vm, - virtio_if_t * vif, u16 queue_id); + virtio_if_t * vif, u16 queue_id, + u16 queue_notify_offset); format_function_t format_virtio_device_name; format_function_t format_virtio_log_name; @@ -208,9 +225,11 @@ virtio_kick (vlib_main_t * vm, virtio_vring_t * vring, virtio_if_t * vif) if (vif->type == VIRTIO_IF_TYPE_PCI) { if (vif->is_modern) - virtio_pci_modern_notify_queue (vm, vif, vring->queue_id); + virtio_pci_modern_notify_queue (vm, vif, vring->queue_id, + vring->queue_notify_offset); else - virtio_pci_legacy_notify_queue (vm, vif, vring->queue_id); + virtio_pci_legacy_notify_queue (vm, vif, vring->queue_id, + vring->queue_notify_offset); } else { diff --git a/src/vnet/devices/virtio/virtio_pci_legacy.c b/src/vnet/devices/virtio/virtio_pci_legacy.c index cf1d84135aa3..1426a7035a21 100644 --- a/src/vnet/devices/virtio/virtio_pci_legacy.c +++ b/src/vnet/devices/virtio/virtio_pci_legacy.c @@ -203,9 +203,16 @@ virtio_pci_legacy_del_queue (vlib_main_t * vm, virtio_if_t * vif, vlib_pci_write_io_u32 (vm, vif->pci_dev_handle, VIRTIO_PCI_QUEUE_PFN, &src); } +static u16 +virtio_pci_legacy_get_queue_notify_off (vlib_main_t * vm, virtio_if_t * vif, + u16 queue_id) +{ + return 0; +} + inline void virtio_pci_legacy_notify_queue (vlib_main_t * vm, virtio_if_t * vif, - u16 queue_id) + u16 queue_id, u16 queue_notify_off) { vlib_pci_write_io_u16 (vm, vif->pci_dev_handle, VIRTIO_PCI_QUEUE_NOTIFY, &queue_id); @@ -364,6 +371,7 @@ const virtio_pci_func_t virtio_pci_legacy_func = { .set_queue_size = virtio_pci_legacy_set_queue_num, .setup_queue = virtio_pci_legacy_setup_queue, .del_queue = virtio_pci_legacy_del_queue, + .get_queue_notify_off = virtio_pci_legacy_get_queue_notify_off, .notify_queue = virtio_pci_legacy_notify_queue, .set_config_irq = virtio_pci_legacy_set_config_irq, .set_queue_irq = virtio_pci_legacy_set_queue_irq, diff --git a/src/vnet/devices/virtio/virtio_pci_modern.c b/src/vnet/devices/virtio/virtio_pci_modern.c index 0d86ff2acda4..1934f98003d6 100644 --- a/src/vnet/devices/virtio/virtio_pci_modern.c +++ b/src/vnet/devices/virtio/virtio_pci_modern.c @@ -208,7 +208,8 @@ virtio_pci_modern_set_queue_enable (virtio_if_t * vif, u16 queue_id, } static u16 -virtio_pci_modern_get_queue_notify_off (virtio_if_t * vif, u16 queue_id) +virtio_pci_modern_get_queue_notify_off (vlib_main_t * vm, virtio_if_t * vif, + u16 queue_id) { u16 queue_notify_off = 0; virtio_pci_modern_set_queue_select (vif, queue_id); @@ -311,19 +312,17 @@ virtio_pci_modern_del_queue (vlib_main_t * vm, virtio_if_t * vif, static void virtio_pci_modern_get_device_mac (vlib_main_t * vm, virtio_if_t * vif) { - *((u32 *) vif->mac_addr) = - virtio_pci_reg_read_u32 (vif, VIRTIO_MAC_OFFSET (vif)); - *((u16 *) (vif->mac_addr + 4)) = + vif->mac_addr32 = virtio_pci_reg_read_u32 (vif, VIRTIO_MAC_OFFSET (vif)); + vif->mac_addr16 = virtio_pci_reg_read_u16 (vif, VIRTIO_MAC_OFFSET (vif) + 4); } static void virtio_pci_modern_set_device_mac (vlib_main_t * vm, virtio_if_t * vif) { - virtio_pci_reg_write_u32 (vif, VIRTIO_MAC_OFFSET (vif), - *((u32 *) vif->mac_addr)); + virtio_pci_reg_write_u32 (vif, VIRTIO_MAC_OFFSET (vif), vif->mac_addr32); virtio_pci_reg_write_u16 (vif, VIRTIO_MAC_OFFSET (vif) + 4, - *((u16 *) (vif->mac_addr + 4))); + vif->mac_addr16); } static u16 @@ -389,14 +388,11 @@ virtio_pci_modern_get_isr (vlib_main_t * vm, virtio_if_t * vif) inline void virtio_pci_modern_notify_queue (vlib_main_t * vm, virtio_if_t * vif, - u16 queue_id) + u16 queue_id, u16 queue_notify_off) { - u16 queue_notify_off = - virtio_pci_modern_get_queue_notify_off (vif, queue_id); virtio_pci_reg_write_u16 (vif, VIRTIO_NOTIFICATION_OFFSET (vif) + - vif->notify_off_multiplier * queue_notify_off, - queue_id); + queue_notify_off, queue_id); } static void @@ -420,6 +416,7 @@ const virtio_pci_func_t virtio_pci_modern_func = { .set_queue_size = virtio_pci_modern_set_queue_size, .setup_queue = virtio_pci_modern_setup_queue, .del_queue = virtio_pci_modern_del_queue, + .get_queue_notify_off = virtio_pci_modern_get_queue_notify_off, .notify_queue = virtio_pci_modern_notify_queue, .set_config_irq = virtio_pci_modern_set_msix_config, .set_queue_irq = virtio_pci_modern_set_queue_msix_vector, diff --git a/src/vnet/feature/feature.c b/src/vnet/feature/feature.c index 1bdd695e1de9..c93f586c349a 100644 --- a/src/vnet/feature/feature.c +++ b/src/vnet/feature/feature.c @@ -37,7 +37,7 @@ vnet_feature_register (vnet_feature_update_cb_t cb, void *data) } static void -vent_feature_reg_invoke (u32 sw_if_index, u8 arc_index, u8 is_enable) +vnet_feature_reg_invoke (u32 sw_if_index, u8 arc_index, u8 is_enable) { vnet_feature_upd_registration_t *reg; @@ -293,7 +293,7 @@ vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index, fm->sw_if_index_has_features[arc_index] = clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index, (feature_count > 0)); - vent_feature_reg_invoke (sw_if_index, arc_index, (feature_count > 0)); + vnet_feature_reg_invoke (sw_if_index, arc_index, (feature_count > 0)); fm->feature_count_by_sw_if_index[arc_index][sw_if_index] = feature_count; return 0; @@ -515,9 +515,7 @@ vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index, int verbose) vlib_cli_output (vm, "\n%s:", areg->arc_name); areg = areg->next; - if (NULL == cm[feature_arc].config_index_by_sw_if_index || - vec_len (cm[feature_arc].config_index_by_sw_if_index) <= - sw_if_index) + if (!vnet_have_features (feature_arc, sw_if_index)) { vlib_cli_output (vm, " none configured"); continue; @@ -525,17 +523,8 @@ vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index, int verbose) current_config_index = vec_elt (cm[feature_arc].config_index_by_sw_if_index, sw_if_index); - - if (current_config_index == ~0) - { - vlib_cli_output (vm, " none configured"); - continue; - } - - ASSERT (current_config_index - < vec_len (vcm->config_pool_index_by_user_index)); - - cfg_index = vcm->config_pool_index_by_user_index[current_config_index]; + cfg_index = + vec_elt (vcm->config_pool_index_by_user_index, current_config_index); cfg = pool_elt_at_index (vcm->config_pool, cfg_index); for (i = 0; i < vec_len (cfg->features); i++) @@ -662,6 +651,52 @@ VLIB_CLI_COMMAND (set_interface_feature_command, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +vnet_feature_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index, + u32 is_add) +{ + vnet_feature_main_t *fm = &feature_main; + const vnet_feature_arc_registration_t *far; + + if (is_add) + return 0; + + /* + * remove all enabled features from an interface on deletion + */ + for (far = fm->next_arc; far != 0; far = far->next) + { + const u8 arc_index = far->feature_arc_index; + vnet_feature_config_main_t *cm = + vec_elt_at_index (fm->feature_config_mains, arc_index); + const u32 ci = + vec_len (cm->config_index_by_sw_if_index) <= + sw_if_index ? ~0 : vec_elt (cm->config_index_by_sw_if_index, + sw_if_index); + + if (~0 == ci) + continue; + + fm->sw_if_index_has_features[arc_index] = + clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index, + 0); + + vnet_feature_reg_invoke (sw_if_index, arc_index, 0); + + if (vec_len (fm->feature_count_by_sw_if_index[arc_index]) > sw_if_index) + vec_elt (fm->feature_count_by_sw_if_index[arc_index], sw_if_index) = + 0; + + vec_elt (cm->config_index_by_sw_if_index, sw_if_index) = ~0; + vnet_config_del (&cm->config_main, ci); + } + + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION_PRIO (vnet_feature_add_del_sw_interface, + VNET_ITF_FUNC_PRIORITY_HIGH); + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/geneve/geneve.api b/src/vnet/geneve/geneve.api index 4502d8907976..ef2cd5ed8ae9 100644 --- a/src/vnet/geneve/geneve.api +++ b/src/vnet/geneve/geneve.api @@ -21,7 +21,7 @@ import "vnet/ip/ip_types.api"; define geneve_add_del_tunnel { - option deprecated="20.06"; + option deprecated; u32 client_index; u32 context; bool is_add; diff --git a/src/vnet/gso/gro.h b/src/vnet/gso/gro.h index bfa592041e55..b2d6c2858885 100644 --- a/src/vnet/gso/gro.h +++ b/src/vnet/gso/gro.h @@ -164,16 +164,7 @@ static_always_inline void gro_flow_table_set_is_enable (gro_flow_table_t * flow_table, u8 is_enable) { if (flow_table) - { - if (is_enable) - { - flow_table->is_enable = 1; - } - else - { - flow_table->is_enable = 0; - } - } + flow_table->is_enable = is_enable; } static_always_inline void @@ -264,12 +255,24 @@ static_always_inline u8 * gro_flow_table_format (u8 * s, va_list * args) { gro_flow_table_t *flow_table = va_arg (*args, gro_flow_table_t *); + u32 indent; + + if (!flow_table) + return s; + + indent = format_get_indent (s); + if (flow_table->is_enable) + s = format (s, "packet-coalesce: enable\n"); + else + s = format (s, "packet-coalesce: disable\n"); + + indent += 2; s = format (s, - "flow-table: size %u gro-total-vectors %lu gro-n-vectors %u", - flow_table->flow_table_size, flow_table->total_vectors, - flow_table->n_vectors); + "%Uflow-table: size %u gro-total-vectors %lu gro-n-vectors %u", + format_white_space, indent, flow_table->flow_table_size, + flow_table->total_vectors, flow_table->n_vectors); if (flow_table->n_vectors) { double average_rate = diff --git a/src/vnet/interface.h b/src/vnet/interface.h index 895ce2e92798..501ea7a6eb6e 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -179,6 +179,8 @@ static __clib_unused void * __clib_unused_##f = f; _VNET_INTERFACE_FUNCTION_DECL(f,sw_interface_mtu_change) #define VNET_SW_INTERFACE_ADD_DEL_FUNCTION(f) \ _VNET_INTERFACE_FUNCTION_DECL(f,sw_interface_add_del) +#define VNET_SW_INTERFACE_ADD_DEL_FUNCTION_PRIO(f,p) \ + _VNET_INTERFACE_FUNCTION_DECL_PRIO(f,sw_interface_add_del,p) #define VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(f) \ _VNET_INTERFACE_FUNCTION_DECL(f,sw_interface_admin_up_down) #define VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION_PRIO(f,p) \ diff --git a/src/vnet/ip-neighbor/ip_neighbor.api b/src/vnet/ip-neighbor/ip_neighbor.api index fe344af5c820..62730e7c1e3c 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.api +++ b/src/vnet/ip-neighbor/ip_neighbor.api @@ -179,8 +179,7 @@ autoreply define ip_neighbor_flush vl_api_interface_index_t sw_if_index [default=0xffffffff]; }; -/** \brief Register for IP4 ARP resolution event on receiving ARP reply or - MAC/IP info from ARP requests in L2 BDs +/** \brief Register for IP neighbour events creation @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param enable - 1 => register for events, 0 => cancel registration @@ -190,6 +189,7 @@ autoreply define ip_neighbor_flush */ autoreply define want_ip_neighbor_events { + option deprecated; u32 client_index; u32 context; bool enable; @@ -206,6 +206,7 @@ autoreply define want_ip_neighbor_events */ define ip_neighbor_event { + option deprecated; u32 client_index; u32 pid; vl_api_ip_neighbor_t neighbor; @@ -216,6 +217,53 @@ service { events ip_neighbor_event; }; + +/** \brief Register for IP neighbour events (creation or deletion) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param enable - 1 => register for events, 0 => cancel registration + @param pid - sender's pid + @param ip - exact IP address of interested neighbor resolution event + @param sw_if_index - interface on which the IP address is present. +*/ +autoreply define want_ip_neighbor_events_v2 +{ + u32 client_index; + u32 context; + bool enable; + u32 pid; + vl_api_address_t ip; + vl_api_interface_index_t sw_if_index [default=0xffffffff]; +}; + +enum ip_neighbor_event_flags +{ + /* The neighbor has been added/learned */ + IP_NEIGHBOR_API_EVENT_FLAG_ADDED = 0x1, + /* The neighbor has been removed/expired */ + IP_NEIGHBOR_API_EVENT_FLAG_REMOVED = 0x2, +}; + +/** \brief Tell client about an IP4 ARP resolution event or + MAC/IP info from ARP requests in L2 BDs + @param client_index - opaque cookie to identify the sender + @param pid - client pid registered to receive notification + @param flags - Flags + @param neighbor - neighbor +*/ +define ip_neighbor_event_v2 +{ + u32 client_index; + u32 pid; + vl_api_ip_neighbor_event_flags_t flags; + vl_api_ip_neighbor_t neighbor; +}; + +service { + rpc want_ip_neighbor_events_v2 returns want_ip_neighbor_events_v2_reply + events ip_neighbor_event_v2; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c index 4111b02d0d89..43d2ccf7f904 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.c +++ b/src/vnet/ip-neighbor/ip_neighbor.c @@ -371,11 +371,14 @@ ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx) } static void -ip_neighbor_free (ip_neighbor_t * ipn) +ip_neighbor_destroy (ip_neighbor_t * ipn) { IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor, ip_neighbor_get_index (ipn)); + ip_neighbor_publish (ip_neighbor_get_index (ipn), + IP_NEIGHBOR_EVENT_REMOVED); + adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index, fib_proto_from_ip46 (ipn->ipn_key->ipnk_type), &ipn->ipn_key->ipnk_ip, @@ -409,7 +412,7 @@ ip_neighbor_force_reuse (ip46_type_t type) return (false); elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head); - ip_neighbor_free (ip_neighbor_get (elt->ipne_index)); + ip_neighbor_destroy (ip_neighbor_get (elt->ipne_index)); return (true); } @@ -540,7 +543,7 @@ ip_neighbor_add (const ip46_address_t * ip, check_customers: /* Customer(s) requesting event for this address? */ - ip_neighbor_publish (ip_neighbor_get_index (ipn)); + ip_neighbor_publish (ip_neighbor_get_index (ipn), IP_NEIGHBOR_EVENT_ADDED); if (stats_index) *stats_index = adj_nbr_find (fproto, @@ -573,7 +576,7 @@ ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index) if (NULL == ipn) return (VNET_API_ERROR_NO_SUCH_ENTRY); - ip_neighbor_free (ipn); + ip_neighbor_destroy (ipn); return (0); } @@ -608,7 +611,8 @@ ip_neighbor_del_all (ip46_type_t type, u32 sw_if_index) ip_neighbor_walk (type, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx); - vec_foreach (ipni, ctx.ipn_del) ip_neighbor_free (ip_neighbor_get (*ipni)); + vec_foreach (ipni, + ctx.ipn_del) ip_neighbor_destroy (ip_neighbor_get (*ipni)); vec_free (ctx.ipn_del); } @@ -1219,7 +1223,7 @@ ip_neighbor_flush (ip46_type_t type, u32 sw_if_index) })); /* *INDENT-ON* */ - vec_foreach (ipni, ipnis) ip_neighbor_free (ip_neighbor_get (*ipni)); + vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni)); vec_free (ipnis); } @@ -1272,7 +1276,7 @@ ip_neighbor_sweep (ip46_type_t type) vec_foreach (ipni, ctx.ipnsc_stale) { - ip_neighbor_free (ip_neighbor_get (*ipni)); + ip_neighbor_destroy (ip_neighbor_get (*ipni)); } vec_free (ctx.ipnsc_stale); } @@ -1395,7 +1399,7 @@ ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im, ip_neighbor_walk_covered, &ctx); vec_foreach (ipni, ctx.ipnis) - ip_neighbor_free (ip_neighbor_get (*ipni)); + ip_neighbor_destroy (ip_neighbor_get (*ipni)); vec_free (ctx.ipnis); } @@ -1434,7 +1438,7 @@ ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im, ip_neighbor_walk_covered, &ctx); vec_foreach (ipni, ctx.ipnis) - ip_neighbor_free (ip_neighbor_get (*ipni)); + ip_neighbor_destroy (ip_neighbor_get (*ipni)); vec_free (ctx.ipnis); } @@ -1607,7 +1611,7 @@ ip_neighbor_age_loop (vlib_main_t * vm, else if (IP_NEIGHBOR_AGE_DEAD == res) { /* the oldest neighbor is dead, pop it, then restart the walk * again from the back */ - ip_neighbor_free (ip_neighbor_get(elt->ipne_index)); + ip_neighbor_destroy (ip_neighbor_get(elt->ipne_index)); goto restart; } diff --git a/src/vnet/ip-neighbor/ip_neighbor.h b/src/vnet/ip-neighbor/ip_neighbor.h index cdceadb08591..af3ac3e9e12d 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.h +++ b/src/vnet/ip-neighbor/ip_neighbor.h @@ -22,8 +22,6 @@ #include -void ip_neighbor_scan_enable_disable (ip_neighbor_scan_arg_t * arg); - /***** * APIs external modules can invoke on the neighbor subsystem @@ -70,7 +68,7 @@ extern void ip_neighbor_sweep (ip46_type_t type); /** * From the watcher to the API to publish a new neighbor */ -extern void ip_neighbor_handle_event (const ip_neighbor_event_t * ipne); +extern void ip_neighbor_handle_event (ip_neighbor_event_t * ipne); /** * The set of function that vnet requires from the IP neighbour module. diff --git a/src/vnet/ip-neighbor/ip_neighbor_api.c b/src/vnet/ip-neighbor/ip_neighbor_api.c index 02952c7ba24f..eb535bc211ed 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_api.c +++ b/src/vnet/ip-neighbor/ip_neighbor_api.c @@ -69,13 +69,12 @@ ip_neighbor_encode (vl_api_ip_neighbor_t * api, const ip_neighbor_t * ipn) } void -ip_neighbor_handle_event (const ip_neighbor_event_t * ipne) +ip_neighbor_handle_event (ip_neighbor_event_t * ipne) { - vl_api_ip_neighbor_event_t *mp; vl_api_registration_t *reg; - const ip_neighbor_t *ipn; + ip_neighbor_t *ipn; - ipn = ip_neighbor_get (ipne->ipne_index); + ipn = &ipne->ipne_nbr; if (NULL == ipn) /* Client can cancel, die, etc. */ @@ -88,15 +87,37 @@ ip_neighbor_handle_event (const ip_neighbor_event_t * ipne) if (vl_api_can_send_msg (reg)) { - mp = vl_msg_api_alloc (sizeof (*mp)); - clib_memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_IP_NEIGHBOR_EVENT + REPLY_MSG_ID_BASE); - mp->client_index = ipne->ipne_watch.ipw_client; - mp->pid = ipne->ipne_watch.ipw_pid; + if (1 == ipne->ipne_watch.ipw_api_version) + { + vl_api_ip_neighbor_event_t *mp; + + mp = vl_msg_api_alloc (sizeof (*mp)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = + ntohs (VL_API_IP_NEIGHBOR_EVENT + REPLY_MSG_ID_BASE); + mp->client_index = ipne->ipne_watch.ipw_client; + mp->pid = ipne->ipne_watch.ipw_pid; - ip_neighbor_encode (&mp->neighbor, ipn); + ip_neighbor_encode (&mp->neighbor, ipn); - vl_api_send_msg (reg, (u8 *) mp); + vl_api_send_msg (reg, (u8 *) mp); + } + else if (2 == ipne->ipne_watch.ipw_api_version) + { + vl_api_ip_neighbor_event_v2_t *mp; + + mp = vl_msg_api_alloc (sizeof (*mp)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = + ntohs (VL_API_IP_NEIGHBOR_EVENT_V2 + REPLY_MSG_ID_BASE); + mp->client_index = ipne->ipne_watch.ipw_client; + mp->pid = ipne->ipne_watch.ipw_pid; + mp->flags = clib_host_to_net_u32 (ipne->ipne_flags); + + ip_neighbor_encode (&mp->neighbor, ipn); + + vl_api_send_msg (reg, (u8 *) mp); + } } else { @@ -107,12 +128,14 @@ ip_neighbor_handle_event (const ip_neighbor_event_t * ipne) */ if (vlib_time_now (vlib_get_main ()) > last_time + 10.0) { - clib_warning ("ip6 nd event for %U to pid %d: queue stuffed!", + clib_warning ("neighbor event for %U to pid %d: queue stuffed!", format_ip46_address, &ipn->ipn_key->ipnk_ip, IP46_TYPE_ANY, ipne->ipne_watch.ipw_pid); last_time = vlib_time_now (vlib_get_main ()); } } + + ip_neighbor_free (ipn); } typedef struct ip_neighbor_dump_ctx_t_ @@ -247,6 +270,7 @@ vl_api_want_ip_neighbor_events_t_handler (vl_api_want_ip_neighbor_events_t * ip_neighbor_watcher_t watch = { .ipw_client = mp->client_index, .ipw_pid = mp->pid, + .ipw_api_version = 1, }; if (mp->enable) @@ -258,6 +282,34 @@ vl_api_want_ip_neighbor_events_t_handler (vl_api_want_ip_neighbor_events_t * REPLY_MACRO (VL_API_WANT_IP_NEIGHBOR_EVENTS_REPLY); } +static void + vl_api_want_ip_neighbor_events_v2_t_handler + (vl_api_want_ip_neighbor_events_v2_t * mp) +{ + vl_api_want_ip_neighbor_events_reply_t *rmp; + ip46_address_t ip; + ip46_type_t itype; + int rv = 0; + + if (mp->sw_if_index != ~0) + VALIDATE_SW_IF_INDEX (mp); + itype = ip_address_decode (&mp->ip, &ip); + + ip_neighbor_watcher_t watch = { + .ipw_client = mp->client_index, + .ipw_pid = mp->pid, + .ipw_api_version = 2, + }; + + if (mp->enable) + ip_neighbor_watch (&ip, itype, ntohl (mp->sw_if_index), &watch); + else + ip_neighbor_unwatch (&ip, itype, ntohl (mp->sw_if_index), &watch); + + BAD_SW_IF_INDEX_LABEL; + REPLY_MACRO (VL_API_WANT_IP_NEIGHBOR_EVENTS_V2_REPLY); +} + static void vl_api_ip_neighbor_config_t_handler (vl_api_ip_neighbor_config_t * mp) { diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.c b/src/vnet/ip-neighbor/ip_neighbor_types.c index 32c674d23924..b4b646bd4351 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_types.c +++ b/src/vnet/ip-neighbor/ip_neighbor_types.c @@ -17,6 +17,21 @@ #include +void +ip_neighbor_clone (const ip_neighbor_t * ipn, ip_neighbor_t * clone) +{ + clib_memcpy (clone, ipn, sizeof (*ipn)); + + clone->ipn_key = clib_mem_alloc (sizeof (ip_neighbor_key_t)); + clib_memcpy (clone->ipn_key, ipn->ipn_key, sizeof (ip_neighbor_key_t)); +} + +void +ip_neighbor_free (ip_neighbor_t * ipn) +{ + clib_mem_free (ipn->ipn_key); +} + u8 * format_ip_neighbor_flags (u8 * s, va_list * args) { diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.h b/src/vnet/ip-neighbor/ip_neighbor_types.h index 82c54177e802..de31705cf9e3 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_types.h +++ b/src/vnet/ip-neighbor/ip_neighbor_types.h @@ -22,21 +22,6 @@ #include #include -#define IP_SCAN_DISABLED 0 -#define IP_SCAN_V4_NEIGHBORS (1 << 0) -#define IP_SCAN_V6_NEIGHBORS (1 << 1) -#define IP_SCAN_V46_NEIGHBORS (IP_SCAN_V4_NEIGHBORS | IP_SCAN_V6_NEIGHBORS) - -typedef struct -{ - u8 mode; /* 0: disable, 1: ip4, 2: ip6, 3: both */ - u8 scan_interval; /* neighbor scan interval in minutes */ - u8 max_proc_time; /* max processing time per run, in usecs */ - u8 max_update; /* max probe/delete operations per run */ - u8 scan_int_delay; /* delay in msecs, to resume scan on max */ - u8 stale_threshold; /* Threshold in minutes to delete nei entry */ -} ip_neighbor_scan_arg_t; - #define foreach_ip_neighbor_flag \ _(STATIC, 1 << 0, "static", "S") \ _(DYNAMIC, 1 << 1, "dynamic", "D") \ @@ -50,12 +35,13 @@ typedef enum ip_neighbor_flags_t_ #define _(a,b,c,d) IP_NEIGHBOR_FLAG_##a = b, foreach_ip_neighbor_flag #undef _ -} __attribute__ ((packed)) ip_neighbor_flags_t; +} __clib_packed ip_neighbor_flags_t; typedef struct ip_neighbor_watcher_t_ { u32 ipw_pid; u32 ipw_client; + int ipw_api_version; } ip_neighbor_watcher_t; extern u8 *format_ip_neighbor_watcher (u8 * s, va_list * args); @@ -116,12 +102,27 @@ typedef struct ip_neighbor_learn_t_ u32 sw_if_index; } ip_neighbor_learn_t; + +typedef enum ip_neighbor_event_flags_t_ +{ + IP_NEIGHBOR_EVENT_ADDED = (1 << 0), + IP_NEIGHBOR_EVENT_REMOVED = (1 << 1), +} ip_neighbor_event_flags_t; + typedef struct ip_neighbor_event_t_ { ip_neighbor_watcher_t ipne_watch; - index_t ipne_index; + ip_neighbor_event_flags_t ipne_flags; + ip_neighbor_t ipne_nbr; } ip_neighbor_event_t; +extern void ip_neighbor_clone (const ip_neighbor_t * ipn, + ip_neighbor_t * clone); + +extern void ip_neighbor_free (ip_neighbor_t * ipn); + + + #endif /* __INCLUDE_IP_NEIGHBOR_H__ */ /* diff --git a/src/vnet/ip-neighbor/ip_neighbor_watch.c b/src/vnet/ip-neighbor/ip_neighbor_watch.c index 7464ee62189f..875aa696cb97 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_watch.c +++ b/src/vnet/ip-neighbor/ip_neighbor_watch.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -178,7 +179,9 @@ ip_neighbor_unwatch (const ip46_address_t * ip, } static void -ip_neighbor_signal (ip_neighbor_watcher_t *watchers, index_t ipni) +ip_neighbor_signal (ip_neighbor_watcher_t *watchers, + index_t ipni, + ip_neighbor_event_flags_t flags) { ip_neighbor_watcher_t *watcher; @@ -189,12 +192,14 @@ ip_neighbor_signal (ip_neighbor_watcher_t *watchers, index_t ipni) ip_neighbor_event_process_node.index, 0, 1, sizeof(*ipne)); ipne->ipne_watch = *watcher; - ipne->ipne_index = ipni; + ipne->ipne_flags = flags; + ip_neighbor_clone(ip_neighbor_get(ipni), &ipne->ipne_nbr); } } void -ip_neighbor_publish (index_t ipni) +ip_neighbor_publish (index_t ipni, + ip_neighbor_event_flags_t flags) { const ip_neighbor_t *ipn; ip_neighbor_key_t key; @@ -208,21 +213,21 @@ ip_neighbor_publish (index_t ipni) p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { - ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni); + ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni, flags); } ip46_address_reset (&key.ipnk_ip); p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { - ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni); + ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni, flags); } key.ipnk_sw_if_index = ~0; p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { - ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni); + ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni, flags); } } diff --git a/src/vnet/ip-neighbor/ip_neighbor_watch.h b/src/vnet/ip-neighbor/ip_neighbor_watch.h index 91d9f6fe12f5..294099fd3c86 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_watch.h +++ b/src/vnet/ip-neighbor/ip_neighbor_watch.h @@ -29,7 +29,8 @@ extern void ip_neighbor_unwatch (const ip46_address_t * ip, u32 sw_if_index, const ip_neighbor_watcher_t * watch); -extern void ip_neighbor_publish (index_t ipni); +extern void ip_neighbor_publish (index_t ipni, + ip_neighbor_event_flags_t flags); #endif diff --git a/src/vnet/ip/ip46_cli.c b/src/vnet/ip/ip46_cli.c index ee797ab70682..f58be898d9bb 100644 --- a/src/vnet/ip/ip46_cli.c +++ b/src/vnet/ip/ip46_cli.c @@ -206,10 +206,10 @@ add_del_ip_address (vlib_main_t * vm, * @cliexcmd{set interface ip address GigabitEthernet2/0/0 @::a:1:1:0:7/126} * * To delete a specific interface ip address: - * @cliexcmd{set interface ip address GigabitEthernet2/0/0 172.16.2.12/24 del} + * @cliexcmd{set interface ip address del GigabitEthernet2/0/0 172.16.2.12/24} * * To delete all interfaces addresses (IPv4 and IPv6): - * @cliexcmd{set interface ip address GigabitEthernet2/0/0 del all} + * @cliexcmd{set interface ip address del GigabitEthernet2/0/0 all} * @endparblock ?*/ /* *INDENT-OFF* */ diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c index c0773e2963f9..629df231601c 100644 --- a/src/vnet/ipsec/esp_encrypt.c +++ b/src/vnet/ipsec/esp_encrypt.c @@ -234,6 +234,24 @@ esp_get_ip6_hdr_len (ip6_header_t * ip6, ip6_ext_header_t ** ext_hdr) return len; } +/* IPsec IV generation: IVs requirements differ depending of the + * encryption mode: IVs must be unpredictable for AES-CBC whereas it can + * be predictable but should never be reused with the same key material + * for CTR and GCM. + * We use a packet counter as the IV for CTR and GCM, and to ensure the + * IV is unpredictable for CBC, it is then encrypted using the same key + * as the message. You can refer to NIST SP800-38a and NIST SP800-38d + * for more details. */ +static_always_inline void * +esp_generate_iv (ipsec_sa_t * sa, void *payload, int iv_sz) +{ + ASSERT (iv_sz >= sizeof (u64)); + u64 *iv = (u64 *) (payload - iv_sz); + clib_memset_u8 (iv, 0, iv_sz); + *iv = sa->iv_counter++; + return iv; +} + static_always_inline void esp_process_chained_ops (vlib_main_t * vm, vlib_node_runtime_t * node, vnet_crypto_op_t * ops, vlib_buffer_t * b[], @@ -396,10 +414,16 @@ esp_prepare_sync_op (vlib_main_t * vm, ipsec_per_thread_data_t * ptd, vnet_crypto_op_t *op; vec_add2_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES); vnet_crypto_op_init (op, sa0->crypto_enc_op_id); + u8 *crypto_start = payload; + /* esp_add_footer_and_icv() in esp_encrypt_inline() makes sure we always + * have enough space for ESP header and footer which includes ICV */ + ASSERT (payload_len > icv_sz); + u16 crypto_len = payload_len - icv_sz; + + /* generate the IV in front of the payload */ + void *pkt_iv = esp_generate_iv (sa0, payload, iv_sz); - op->src = op->dst = payload; op->key_index = sa0->crypto_key_index; - op->len = payload_len - icv_sz; op->user_data = b - bufs; if (ipsec_sa_is_set_IS_AEAD (sa0)) @@ -411,18 +435,21 @@ esp_prepare_sync_op (vlib_main_t * vm, ipsec_per_thread_data_t * ptd, op->aad = payload - hdr_len - sizeof (esp_aead_t); op->aad_len = esp_aad_fill (op->aad, esp, sa0); - op->tag = payload + op->len; + op->tag = payload + crypto_len; op->tag_len = 16; - u64 *iv = (u64 *) (payload - iv_sz); nonce->salt = sa0->salt; - nonce->iv = *iv = clib_host_to_net_u64 (sa0->gcm_iv_counter++); + nonce->iv = *(u64 *) pkt_iv; op->iv = (u8 *) nonce; } else { - op->iv = payload - iv_sz; - op->flags = VNET_CRYPTO_OP_FLAG_INIT_IV; + /* construct zero iv in front of the IP header */ + op->iv = pkt_iv - hdr_len - iv_sz; + clib_memset_u8 (op->iv, 0, iv_sz); + /* include iv field in crypto */ + crypto_start -= iv_sz; + crypto_len += iv_sz; } if (lb != b[0]) @@ -431,8 +458,15 @@ esp_prepare_sync_op (vlib_main_t * vm, ipsec_per_thread_data_t * ptd, op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS; op->chunk_index = vec_len (ptd->chunks); op->tag = vlib_buffer_get_tail (lb) - icv_sz; - esp_encrypt_chain_crypto (vm, ptd, sa0, b[0], lb, icv_sz, payload, - payload_len, &op->n_chunks); + esp_encrypt_chain_crypto (vm, ptd, sa0, b[0], lb, icv_sz, + crypto_start, crypto_len + icv_sz, + &op->n_chunks); + } + else + { + /* not chained */ + op->src = op->dst = crypto_start; + op->len = crypto_len; } } @@ -482,17 +516,20 @@ esp_prepare_async_frame (vlib_main_t * vm, ipsec_per_thread_data_t * ptd, u8 *tag, *iv, *aad = 0; u8 flag = 0; u32 key_index; - i16 crypto_start_offset, integ_start_offset = 0; + i16 crypto_start_offset, integ_start_offset; u16 crypto_total_len, integ_total_len; post->next_index = next[0]; next[0] = ESP_ENCRYPT_NEXT_PENDING; /* crypto */ - crypto_start_offset = payload - b->data; + crypto_start_offset = integ_start_offset = payload - b->data; crypto_total_len = integ_total_len = payload_len - icv_sz; tag = payload + crypto_total_len; + /* generate the IV in front of the payload */ + void *pkt_iv = esp_generate_iv (sa, payload, iv_sz); + /* aead */ if (ipsec_sa_is_set_IS_AEAD (sa)) { @@ -503,7 +540,7 @@ esp_prepare_async_frame (vlib_main_t * vm, ipsec_per_thread_data_t * ptd, esp_aad_fill (aad, esp, sa); nonce = (esp_gcm_nonce_t *) (aad - sizeof (*nonce)); nonce->salt = sa->salt; - nonce->iv = *pkt_iv = clib_host_to_net_u64 (sa->gcm_iv_counter++); + nonce->iv = *(u64 *) pkt_iv; iv = (u8 *) nonce; key_index = sa->crypto_key_index; @@ -513,25 +550,38 @@ esp_prepare_async_frame (vlib_main_t * vm, ipsec_per_thread_data_t * ptd, flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS; tag = vlib_buffer_get_tail (lb) - icv_sz; crypto_total_len = esp_encrypt_chain_crypto (vm, ptd, sa, b, lb, - icv_sz, payload, - payload_len, 0); + icv_sz, + b->data + + crypto_start_offset, + crypto_total_len + + icv_sz, 0); } goto out; } + else + { + /* construct zero iv in front of the IP header */ + iv = pkt_iv - hdr_len - iv_sz; + clib_memset_u8 (iv, 0, iv_sz); + /* include iv field in crypto */ + crypto_start_offset -= iv_sz; + crypto_total_len += iv_sz; + } /* cipher then hash */ - iv = payload - iv_sz; - integ_start_offset = crypto_start_offset - iv_sz - sizeof (esp_header_t); + integ_start_offset -= iv_sz + sizeof (esp_header_t); integ_total_len += iv_sz + sizeof (esp_header_t); - flag |= VNET_CRYPTO_OP_FLAG_INIT_IV; key_index = sa->linked_key_index; if (b != lb) { flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS; crypto_total_len = esp_encrypt_chain_crypto (vm, ptd, sa, b, lb, - icv_sz, payload, - payload_len, 0); + icv_sz, + b->data + + crypto_start_offset, + crypto_total_len + icv_sz, + 0); tag = vlib_buffer_get_tail (lb) - icv_sz; integ_total_len = esp_encrypt_chain_integ (vm, ptd, sa, b, lb, icv_sz, payload - iv_sz - diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api index 52a16ede1aac..89dcdb761de6 100644 --- a/src/vnet/ipsec/ipsec.api +++ b/src/vnet/ipsec/ipsec.api @@ -343,7 +343,7 @@ define ipsec_spd_interface_details { @param salt - for use with counter mode ciphers */ define ipsec_tunnel_if_add_del { - option deprecated="20.09"; + option deprecated; u32 client_index; u32 context; bool is_add; @@ -491,7 +491,7 @@ define ipsec_sa_details { @param is_outbound - 1 if outbound (local) SA, 0 if inbound (remote) */ autoreply define ipsec_tunnel_if_set_sa { - option deprecated="20.09"; + option deprecated; u32 client_index; u32 context; vl_api_interface_index_t sw_if_index; diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c index 0d1ab033aec7..03727855b4f4 100644 --- a/src/vnet/ipsec/ipsec_cli.c +++ b/src/vnet/ipsec/ipsec_cli.c @@ -36,6 +36,7 @@ set_interface_spd_command_fn (vlib_main_t * vm, u32 spd_id; int is_add = 1; clib_error_t *error = NULL; + int err; if (!unformat_user (input, unformat_line_input, line_input)) return 0; @@ -53,7 +54,16 @@ set_interface_spd_command_fn (vlib_main_t * vm, goto done; } - ipsec_set_interface_spd (vm, sw_if_index, spd_id, is_add); + err = ipsec_set_interface_spd (vm, sw_if_index, spd_id, is_add); + switch (err) + { + case VNET_API_ERROR_SYSCALL_ERROR_1: + error = clib_error_return (0, "no such spd-id"); + break; + case VNET_API_ERROR_SYSCALL_ERROR_2: + error = clib_error_return (0, "spd already assigned"); + break; + } done: unformat_free (line_input); @@ -90,6 +100,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm, u16 udp_src, udp_dst; int is_add, rv; u32 m_args = 0; + u32 tx_table_id; salt = 0; error = NULL; @@ -99,6 +110,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm, integ_alg = IPSEC_INTEG_ALG_NONE; crypto_alg = IPSEC_CRYPTO_ALG_NONE; udp_src = udp_dst = IPSEC_UDP_PORT_NONE; + tx_table_id = 0; if (!unformat_user (input, unformat_line_input, line_input)) return 0; @@ -144,6 +156,8 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm, else if (unformat (line_input, "tunnel-dst %U", unformat_ip46_address, &tun_dst, IP46_TYPE_ANY)) ; + else if (unformat (line_input, "tx-table-id %d", &tx_table_id)) + ; else if (unformat (line_input, "inbound")) flags |= IPSEC_SA_FLAG_IS_INBOUND; else if (unformat (line_input, "use-anti-replay")) @@ -181,7 +195,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm, } rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &ck, integ_alg, &ik, flags, - 0, clib_host_to_net_u32 (salt), + tx_table_id, clib_host_to_net_u32 (salt), &tun_src, &tun_dst, &sai, udp_src, udp_dst); } else diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c index e3c6f22bf00b..f365d0cd6204 100644 --- a/src/vnet/ipsec/ipsec_format.c +++ b/src/vnet/ipsec/ipsec_format.c @@ -95,7 +95,7 @@ format_ipsec_crypto_alg (u8 * s, va_list * args) uword unformat_ipsec_crypto_alg (unformat_input_t * input, va_list * args) { - u32 *r = va_arg (*args, u32 *); + ipsec_crypto_alg_t *r = va_arg (*args, ipsec_crypto_alg_t *); if (0); #define _(v,f,s) else if (unformat (input, s)) *r = IPSEC_CRYPTO_ALG_##f; @@ -127,7 +127,7 @@ format_ipsec_integ_alg (u8 * s, va_list * args) uword unformat_ipsec_integ_alg (unformat_input_t * input, va_list * args) { - u32 *r = va_arg (*args, u32 *); + ipsec_integ_alg_t *r = va_arg (*args, ipsec_integ_alg_t *); if (0); #define _(v,f,s) else if (unformat (input, s)) *r = IPSEC_INTEG_ALG_##f; diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h index 5d65238b7369..24139a9dfbb8 100644 --- a/src/vnet/ipsec/ipsec_sa.h +++ b/src/vnet/ipsec/ipsec_sa.h @@ -182,7 +182,7 @@ typedef struct /* Salt used in GCM modes - stored in network byte order */ u32 salt; - u64 gcm_iv_counter; + u64 iv_counter; union { diff --git a/src/vnet/ipsec/ipsec_spd.c b/src/vnet/ipsec/ipsec_spd.c index ef41c2286c14..4e8017c35ff6 100644 --- a/src/vnet/ipsec/ipsec_spd.c +++ b/src/vnet/ipsec/ipsec_spd.c @@ -77,7 +77,7 @@ ipsec_set_interface_spd (vlib_main_t * vm, u32 sw_if_index, u32 spd_id, p = hash_get (im->spd_index_by_sw_if_index, sw_if_index); if (p && is_add) - return VNET_API_ERROR_SYSCALL_ERROR_1; /* spd already assigned */ + return VNET_API_ERROR_SYSCALL_ERROR_2; /* spd already assigned */ if (is_add) { diff --git a/src/vnet/l2/l2_bvi.h b/src/vnet/l2/l2_bvi.h index 300ea81bbf06..697e25bf3dea 100644 --- a/src/vnet/l2/l2_bvi.h +++ b/src/vnet/l2/l2_bvi.h @@ -72,7 +72,7 @@ l2_to_bvi (vlib_main_t * vlib_main, ethernet_interface_t *ei = ethernet_get_interface (em, hi->hw_if_index); u32 rv; - if (PREDICT_FALSE (vec_len (ei->secondary_addrs) > 0)) + if (PREDICT_FALSE (ei && (vec_len (ei->secondary_addrs) > 0))) rv = l2_to_bvi_dmac_check (hi, e0->dst_address, ei, 1 /* have_sec_dmac */ ); else diff --git a/src/vnet/lisp-cp/control.c b/src/vnet/lisp-cp/control.c index 7e9d059f9ed9..60d85619dc51 100644 --- a/src/vnet/lisp-cp/control.c +++ b/src/vnet/lisp-cp/control.c @@ -3552,7 +3552,11 @@ lisp_cp_lookup_inline (vlib_main_t * vm, sizeof (*tr)); clib_memset (tr, 0, sizeof (*tr)); - gid_address_copy (&tr->dst_eid, &dst); + if ((gid_address_type (&dst) == GID_ADDR_NDP) || + (gid_address_type (&dst) == GID_ADDR_ARP)) + clib_memcpy (&tr->dst_eid, &dst, sizeof (gid_address_t)); + else + gid_address_copy (&tr->dst_eid, &dst); ip_address_copy (&tr->map_resolver_ip, &lcm->active_map_resolver); } diff --git a/src/vnet/lisp-cp/lisp_cli.c b/src/vnet/lisp-cp/lisp_cli.c index 817fb50156b6..93422fe2de7e 100644 --- a/src/vnet/lisp-cp/lisp_cli.c +++ b/src/vnet/lisp-cp/lisp_cli.c @@ -887,7 +887,7 @@ lisp_show_eid_table_command_fn (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (lisp_cp_show_eid_table_command) = { .path = "show lisp eid-table", - .short_help = "Shows EID table", + .short_help = "show lisp eid-table [local|remote|eid ]", .function = lisp_show_eid_table_command_fn, }; /* *INDENT-ON* */ @@ -1128,7 +1128,7 @@ lisp_show_eid_table_map_command_fn (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (lisp_show_eid_table_map_command) = { .path = "show lisp eid-table map", - .short_help = "show lisp eid-table l2|l3", + .short_help = "show lisp eid-table map l2|l3", .function = lisp_show_eid_table_map_command_fn, }; /* *INDENT-ON* */ diff --git a/src/vnet/lisp-cp/lisp_types.c b/src/vnet/lisp-cp/lisp_types.c index 971d3071b6a0..6ff3b4ebf4b7 100644 --- a/src/vnet/lisp-cp/lisp_types.c +++ b/src/vnet/lisp-cp/lisp_types.c @@ -41,7 +41,7 @@ serdes_fct write_fcts[GID_ADDR_TYPES] = }; cast_fct cast_fcts[GID_ADDR_TYPES] = - { ip_prefix_cast, lcaf_cast, mac_cast, sd_cast, nsh_cast, 0 /* arp */ , + { ip_prefix_cast, lcaf_cast, mac_cast, sd_cast, nsh_cast, no_addr_cast, no_addr_cast }; @@ -51,7 +51,7 @@ addr_len_fct addr_len_fcts[GID_ADDR_TYPES] = }; copy_fct copy_fcts[GID_ADDR_TYPES] = - { ip_prefix_copy, lcaf_copy, mac_copy, sd_copy, nsh_copy, 0 /* arp */ , + { ip_prefix_copy, lcaf_copy, mac_copy, sd_copy, nsh_copy, no_addr_copy, no_addr_copy }; diff --git a/src/vnet/lisp-cp/one_api.c b/src/vnet/lisp-cp/one_api.c index f7a972be7a29..452a756ee800 100644 --- a/src/vnet/lisp-cp/one_api.c +++ b/src/vnet/lisp-cp/one_api.c @@ -840,7 +840,7 @@ send_one_eid_table_details (mapping_t * mapit, if (mapit->pitr_set || mapit->nsh_set) return; - switch (ntohl (filter)) + switch (filter) { case ONE_FILTER_API_ALL: /* all mappings */ break; diff --git a/src/vnet/lisp-cp/one_cli.c b/src/vnet/lisp-cp/one_cli.c index e44632f97603..70de0c1a5549 100644 --- a/src/vnet/lisp-cp/one_cli.c +++ b/src/vnet/lisp-cp/one_cli.c @@ -1203,7 +1203,7 @@ lisp_show_eid_table_command_fn (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (one_cp_show_eid_table_command) = { .path = "show one eid-table", - .short_help = "Shows EID table", + .short_help = "show one eid-table [local|remote|eid ]", .function = lisp_show_eid_table_command_fn, }; /* *INDENT-ON* */ @@ -1670,7 +1670,7 @@ lisp_show_eid_table_map_command_fn (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (one_show_eid_table_map_command) = { .path = "show one eid-table map", - .short_help = "show one eid-table l2|l3", + .short_help = "show one eid-table map l2|l3", .function = lisp_show_eid_table_map_command_fn, }; /* *INDENT-ON* */ diff --git a/src/vnet/syslog/syslog.c b/src/vnet/syslog/syslog.c index f05ce8dc5ff2..20728b8a17a2 100644 --- a/src/vnet/syslog/syslog.c +++ b/src/vnet/syslog/syslog.c @@ -143,8 +143,7 @@ void syslog_msg_init (syslog_msg_t * syslog_msg, syslog_facility_t facility, syslog_severity_t severity, char *app_name, char *msgid) { - syslog_main_t *sm = &syslog_main; - vlib_main_t *vm = sm->vlib_main; + vlib_main_t *vm = vlib_get_main (); syslog_msg->header.facility = facility; syslog_msg->header.severity = severity; @@ -160,7 +159,7 @@ int syslog_msg_send (syslog_msg_t * syslog_msg) { syslog_main_t *sm = &syslog_main; - vlib_main_t *vm = sm->vlib_main; + vlib_main_t *vm = vlib_get_main (); u32 bi, msg_len, *to_next; u8 *tmp; vlib_buffer_t *b; @@ -611,7 +610,6 @@ syslog_init (vlib_main_t * vm) struct timeval timeval_0; vlib_node_t *ip4_lookup_node; - sm->vlib_main = vm; sm->vnet_main = vnet_get_main (); sm->procid = getpid (); diff --git a/src/vnet/syslog/syslog.h b/src/vnet/syslog/syslog.h index 4809af48e972..3ec1cb35f355 100644 --- a/src/vnet/syslog/syslog.h +++ b/src/vnet/syslog/syslog.h @@ -136,7 +136,6 @@ typedef struct u32 ip4_lookup_node_index; /** convenience variables */ - vlib_main_t *vlib_main; vnet_main_t *vnet_main; } syslog_main_t; diff --git a/src/vnet/vxlan-gbp/vxlan_gbp.api b/src/vnet/vxlan-gbp/vxlan_gbp.api index c3a8845dec34..685666970003 100644 --- a/src/vnet/vxlan-gbp/vxlan_gbp.api +++ b/src/vnet/vxlan-gbp/vxlan_gbp.api @@ -56,6 +56,7 @@ define vxlan_gbp_tunnel_add_del u32 context; bool is_add [default=true]; vl_api_vxlan_gbp_tunnel_t tunnel; + option in_progress; }; define vxlan_gbp_tunnel_add_del_reply @@ -63,6 +64,7 @@ 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 @@ -70,12 +72,14 @@ 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 @@ -92,4 +96,5 @@ autoreply define sw_interface_set_vxlan_gbp_bypass vl_api_interface_index_t sw_if_index; bool is_ipv6; bool enable [default=true]; + option in_progress; }; diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index 56ff387d3431..018cce342284 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -192,6 +192,16 @@ stat_segment_heartbeat (void) return stat_segment_heartbeat_r (sm); } +#define stat_vec_dup(S,V) \ + ({ \ + __typeof__ ((V)[0]) * _v(v) = 0; \ + if (V && ((void *)V > (void *)S->shared_header) && \ + (((void*)V + vec_bytes(V)) < \ + ((void *)S->shared_header + S->memory_size))) \ + _v(v) = vec_dup(V); \ + _v(v); \ +}) + static stat_segment_data_t copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) { @@ -213,21 +223,21 @@ copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: simple_c = stat_segment_adjust (sm, ep->data); - result.simple_counter_vec = vec_dup (simple_c); + result.simple_counter_vec = stat_vec_dup (sm, simple_c); for (i = 0; i < vec_len (simple_c); i++) { counter_t *cb = stat_segment_adjust (sm, simple_c[i]); - result.simple_counter_vec[i] = vec_dup (cb); + result.simple_counter_vec[i] = stat_vec_dup (sm, cb); } break; case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: combined_c = stat_segment_adjust (sm, ep->data); - result.combined_counter_vec = vec_dup (combined_c); + result.combined_counter_vec = stat_vec_dup (sm, combined_c); for (i = 0; i < vec_len (combined_c); i++) { vlib_counter_t *cb = stat_segment_adjust (sm, combined_c[i]); - result.combined_counter_vec[i] = vec_dup (cb); + result.combined_counter_vec[i] = stat_vec_dup (sm, cb); } break; @@ -246,11 +256,11 @@ copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) case STAT_DIR_TYPE_NAME_VECTOR: { uint8_t **name_vector = stat_segment_adjust (sm, ep->data); - result.name_vector = vec_dup (name_vector); + result.name_vector = stat_vec_dup (sm, name_vector); for (i = 0; i < vec_len (name_vector); i++) { u8 *name = stat_segment_adjust (sm, name_vector[i]); - result.name_vector[i] = vec_dup (name); + result.name_vector[i] = stat_vec_dup (sm, name); } } break; @@ -290,6 +300,8 @@ stat_segment_data_free (stat_segment_data_t * res) case STAT_DIR_TYPE_ERROR_INDEX: vec_free (res[i].error_vector); break; + case STAT_DIR_TYPE_SCALAR_INDEX: + break; default: assert (0); } diff --git a/src/vpp-api/client/stat_client.h b/src/vpp-api/client/stat_client.h index c5fa55977585..b2457ad6e48b 100644 --- a/src/vpp-api/client/stat_client.h +++ b/src/vpp-api/client/stat_client.h @@ -101,8 +101,13 @@ _time_now_nsec (void) static inline void * stat_segment_adjust (stat_client_main_t * sm, void *data) { - return (void *) ((char *) sm->shared_header + - ((char *) data - (char *) sm->shared_header->base)); + void *p = (void *) ((char *) sm->shared_header + + ((char *) data - (char *) sm->shared_header->base)); + if ((char *) p > (char *) sm->shared_header && + (((char *) p + sizeof (p)) < + ((char *) sm->shared_header + sm->memory_size))) + return p; + return 0; } /* diff --git a/src/vpp/stats/stat_segment.c b/src/vpp/stats/stat_segment.c index 8255b16c0100..788d2fddb2cd 100644 --- a/src/vpp/stats/stat_segment.c +++ b/src/vpp/stats/stat_segment.c @@ -282,7 +282,8 @@ stat_set_simple_counter (stat_segment_directory_entry_t * ep, u32 thread_index, u32 index, u64 value) { ASSERT (ep->data); - counter_t *cb = ep->data; + counter_t **counters = ep->data; + counter_t *cb = counters[thread_index]; cb[index] = value; } diff --git a/src/vpp/vnet/main.c b/src/vpp/vnet/main.c index 00cb4ad385ab..7d87d0cd3b10 100644 --- a/src/vpp/vnet/main.c +++ b/src/vpp/vnet/main.c @@ -282,11 +282,8 @@ main (int argc, char *argv[]) /* Allocate main heap */ if ((main_heap = clib_mem_init_thread_safe (0, main_heap_size))) { - vlib_worker_thread_t tmp; - /* Figure out which numa runs the main thread */ - vlib_get_thread_core_numa (&tmp, main_core); - __os_numa_index = tmp.numa_id; + __os_numa_index = clib_get_current_numa_node (); /* and use the main heap as that numa's numa heap */ clib_mem_set_per_numa_heap (main_heap); diff --git a/src/vppinfra/lock.h b/src/vppinfra/lock.h index 3cfe11cba176..470890b6ec76 100644 --- a/src/vppinfra/lock.h +++ b/src/vppinfra/lock.h @@ -91,6 +91,15 @@ clib_spinlock_lock (clib_spinlock_t * p) CLIB_LOCK_DBG (p); } +static_always_inline int +clib_spinlock_trylock (clib_spinlock_t * p) +{ + if (PREDICT_FALSE (CLIB_SPINLOCK_IS_LOCKED (p))) + return 0; + clib_spinlock_lock (p); + return 1; +} + static_always_inline void clib_spinlock_lock_if_init (clib_spinlock_t * p) { @@ -98,6 +107,14 @@ clib_spinlock_lock_if_init (clib_spinlock_t * p) clib_spinlock_lock (p); } +static_always_inline int +clib_spinlock_trylock_if_init (clib_spinlock_t * p) +{ + if (PREDICT_FALSE (*p != 0)) + return clib_spinlock_trylock (p); + return 1; +} + static_always_inline void clib_spinlock_unlock (clib_spinlock_t * p) { diff --git a/test/ext/Makefile b/test/ext/Makefile index d7acb7bc2b28..a0dc3e7736ca 100644 --- a/test/ext/Makefile +++ b/test/ext/Makefile @@ -8,7 +8,7 @@ VAPI_LIBS = \ -L$(VPP_INSTALL_PATH)/vpp/lib \ -lvppinfra -lvlibmemoryclient -lsvm -lpthread -lcheck -lrt -lm -lvapiclient -ifneq ($(filter centos opensuse opensuse-tumbleweed opensuse-leap,$(OS_ID)),$(OS_ID)) +ifneq ($(filter centos,$(OS_ID)),$(OS_ID)) VAPI_LIBS += -lsubunit endif diff --git a/test/patches/scapy-2.4.3/ikev2.patch b/test/patches/scapy-2.4.3/ikev2.patch new file mode 100644 index 000000000000..be143e8b8c9d --- /dev/null +++ b/test/patches/scapy-2.4.3/ikev2.patch @@ -0,0 +1,24 @@ +diff --git a/scapy/contrib/ikev2.py b/scapy/contrib/ikev2.py +index 60b20480..a071ffc7 100644 +--- a/scapy/contrib/ikev2.py ++++ b/scapy/contrib/ikev2.py +@@ -608,13 +608,16 @@ class IKEv2_payload_TSr(IKEv2_class): + + + class IKEv2_payload_Delete(IKEv2_class): +- name = "IKEv2 Vendor ID" ++ name = "IKEv2 delete payload" + overload_fields = {IKEv2: {"next_payload": 42}} + fields_desc = [ + ByteEnumField("next_payload", None, IKEv2_payload_type), + ByteField("res", 0), +- FieldLenField("length", None, "vendorID", "H", adjust=lambda pkt, x:x + 4), # noqa: E501 +- StrLenField("vendorID", "", length_from=lambda x:x.length - 4), ++ FieldLenField("length", None, "SPIs", "H", adjust=lambda pkt, x:x + 8), # noqa: E501 ++ ByteEnumField("proto", 1, {1: "IKEv2", 2: "AH", 3: "ESP"}), ++ ByteField("SPIsize", 0), ++ ShortField("SPInum", 0), ++ StrLenField("SPIs", "", length_from=lambda x: x.length - 8), + ] + + diff --git a/test/test_neighbor.py b/test/test_neighbor.py index 1045f4ba1e9b..c0af91013baf 100644 --- a/test/test_neighbor.py +++ b/test/test_neighbor.py @@ -1959,16 +1959,51 @@ def test_age(self): # # load up some neighbours again with 2s aging enabled # they should be removed after 10s (2s age + 4s for probes + gap) + # check for the add and remove events # + enum = VppEnum.vl_api_ip_neighbor_event_flags_t + + self.vapi.want_ip_neighbor_events_v2(enable=1) for ii in range(10): VppNeighbor(self, self.pg0.sw_if_index, self.pg0.remote_hosts[ii].mac, self.pg0.remote_hosts[ii].ip4).add_vpp_config() + + e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2") + self.assertEqual(e.flags, + enum.IP_NEIGHBOR_API_EVENT_FLAG_ADDED) + self.assertEqual(str(e.neighbor.ip_address), + self.pg0.remote_hosts[ii].ip4) + self.assertEqual(e.neighbor.mac_address, + self.pg0.remote_hosts[ii].mac) + self.sleep(10) self.assertFalse(self.vapi.ip_neighbor_dump(sw_if_index=0xffffffff, af=vaf.ADDRESS_IP4)) + evs = [] + for ii in range(10): + e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2") + self.assertEqual(e.flags, + enum.IP_NEIGHBOR_API_EVENT_FLAG_REMOVED) + evs.append(e) + + # check we got the correct mac/ip pairs - done separately + # because we don't care about the order the remove notifications + # arrive + for ii in range(10): + found = False + mac = self.pg0.remote_hosts[ii].mac + ip = self.pg0.remote_hosts[ii].ip4 + + for e in evs: + if (e.neighbor.mac_address == mac and + str(e.neighbor.ip_address) == ip): + found = True + break + self.assertTrue(found) + # # check if we can set age and recycle with empty neighbor list #