aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeongik Cha <jeongik@google.com>2023-09-27 11:05:42 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-09-27 11:05:42 +0000
commite214d9d0858923956594e8710e6277511dc07674 (patch)
tree79afbdd470ba00de781e84f642644d02662b849b
parent27910099dff0d15e4321d6c1515219e33e504f0a (diff)
parentcfa87f541601b64201894863f645e14337034e2b (diff)
downloadvirtio-vsock-e214d9d0858923956594e8710e6277511dc07674.tar.gz
Import virtio-vsock am: f364c156ca am: d8f6fc7bb7 am: 7b765b5dfc am: cfa87f5416
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/virtio-vsock/+/2754251 Change-Id: Ic337b07ac54ca7633300bb907cc2656f17e09c3b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp18
-rw-r--r--CHANGELOG.md35
-rw-r--r--Cargo.toml48
-rw-r--r--Cargo.toml.orig20
-rw-r--r--LICENSE232
-rw-r--r--LICENSE-BSD-3-Clause27
-rw-r--r--LICENSE_APACHE202
-rw-r--r--METADATA19
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--MODULE_LICENSE_BSD0
-rw-r--r--OWNERS1
-rw-r--r--README.md63
-rw-r--r--cargo2android.json6
-rw-r--r--src/lib.rs11
-rw-r--r--src/packet.rs1397
16 files changed, 2085 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..b29232a
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "854e098e4871f2ea487b1853bc21f02d6c1e8057"
+ },
+ "path_in_vcs": "crates/devices/virtio-vsock"
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..8b0ce37
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,18 @@
+// This file is generated by cargo2android.py --config cargo2android.json.
+// Do not modify this file as changes will be overridden on upgrade.
+
+
+
+rust_library_host {
+ name: "libvirtio_vsock",
+ crate_name: "virtio_vsock",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.3.1",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libvirtio_bindings",
+ "libvirtio_queue",
+ "libvm_memory_android",
+ ],
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..890b8fd
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,35 @@
+# v0.3.1
+
+## Changes
+
+- Updated vm-memory from 0.11.0 to 0.12.0.
+
+# v0.3.0
+
+## Changes
+
+- Updated vm-memory from 0.10.0 to 0.11.0.
+- Updated virtio-queue from 0.7.0 to 0.8.0.
+
+# v0.2.1
+
+## Changes
+
+- `VsockPacket::from_tx_virtq_chain` supports header and data on the same descriptor
+- `VsockPacket::from_rx_virtq_chain` supports header and data on the same descriptor
+
+# v0.2.0
+
+## Added
+
+- Derive `Eq` for `packet::PacketHeader`.
+
+## Changes
+
+- Updated vm-memory to 0.10.0.
+- Updated virtio-queue to 0.7.0.
+- Upgrade Rust edition to 2021.
+
+# v0.1.0
+
+This is the first release of the crate.
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..4bdb1d4
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,48 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "virtio-vsock"
+version = "0.3.1"
+authors = [
+ "rust-vmm community",
+ "rust-vmm AWS maintainers <rust-vmm-maintainers@amazon.com>",
+]
+description = "virtio vsock device implementation"
+readme = "README.md"
+keywords = [
+ "virtio",
+ "vsock",
+]
+license = "Apache-2.0 OR BSD-3-Clause"
+repository = "https://github.com/rust-vmm/vm-virtio"
+resolver = "1"
+
+[dependencies.virtio-bindings]
+version = "0.2.1"
+
+[dependencies.virtio-queue]
+version = "0.9.0"
+
+[dependencies.vm-memory]
+version = "0.12.0"
+
+[dev-dependencies.virtio-queue]
+version = "0.9.0"
+features = ["test-utils"]
+
+[dev-dependencies.vm-memory]
+version = "0.12.0"
+features = [
+ "backend-mmap",
+ "backend-atomic",
+]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..140f7d3
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,20 @@
+[package]
+name = "virtio-vsock"
+version = "0.3.1"
+authors = ["rust-vmm community", "rust-vmm AWS maintainers <rust-vmm-maintainers@amazon.com>"]
+description = "virtio vsock device implementation"
+repository = "https://github.com/rust-vmm/vm-virtio"
+keywords = ["virtio", "vsock"]
+readme = "README.md"
+license = "Apache-2.0 OR BSD-3-Clause"
+edition = "2021"
+
+[dependencies]
+# The `path` part gets stripped when publishing the crate.
+virtio-queue = { path = "../../virtio-queue", version = "0.9.0" }
+virtio-bindings = { path = "../../virtio-bindings", version = "0.2.1" }
+vm-memory = "0.12.0"
+
+[dev-dependencies]
+virtio-queue = { path = "../../virtio-queue", version = "0.9.0", features = ["test-utils"] }
+vm-memory = { version = "0.12.0", features = ["backend-mmap", "backend-atomic"] }
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c57a296
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,232 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+---
+
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE-BSD-3-Clause b/LICENSE-BSD-3-Clause
new file mode 100644
index 0000000..8bafca3
--- /dev/null
+++ b/LICENSE-BSD-3-Clause
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE_APACHE b/LICENSE_APACHE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE_APACHE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..bcb3501
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+name: "virtio-vsock"
+description: "virtio vsock device implementation"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "https://crates.io/crates/virtio-vsock"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/virtio-vsock/virtio-vsock-0.3.1.crate"
+ }
+ version: "0.3.1"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2023
+ month: 8
+ day: 23
+ }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..45dc4dd
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:master:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0819108
--- /dev/null
+++ b/README.md
@@ -0,0 +1,63 @@
+# virtio-vsock
+
+The `virtio-vsock` crate provides abstractions for the components of the
+[vsock device](https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-39000010).
+For now, it offers only an implementation for the vsock packet.
+The support is provided only for stream sockets.
+
+The vsock device is a socket device that can be used as a communication
+mechanism between the host and the guest. It is implemented using the virtio
+standard. The vsock device has three queues: an RX one, a TX one and an event
+one. In a simplified manner, the communication between the host and the guest
+is realized with the buffers that are exchanged using the device’s queues.
+These buffers are called packets in the vsock device context.
+
+## Vsock Packet
+
+### Design
+
+The virtio vsock packet is defined in the standard as having a header of type
+`virtio_vsock_hdr` and an optional `data` array of bytes. There are multiple
+operations that can be requested within a packet, e.g. `VIRTIO_VSOCK_OP_RST`
+for resetting the connection, `VIRTIO_VSOCK_OP_RW` for sending payload. Most
+operations are of the `VIRTIO_VSOCK_OP_RW` type, which means for data transfer,
+and the other ones are used for connection and buffer space management.
+`data` is non-empty only for the `VIRTIO_VSOCK_OP_RW` operations.
+
+The abstraction used for the packet implementation is the `VsockPacket`.
+It is using
+[`VolatileSlice`](https://github.com/rust-vmm/vm-memory/blob/fc7153a4f63c352d1fa9419c4654a6c9aec408cb/src/volatile_memory.rs#L266)s
+for representing the header and the data. We chose to use the `VolatileSlice`
+because it's a safe wrapper over the unsafe Rust's raw pointers, and it is also
+generic enough to allow creating packets from pointers to slices. Going with a
+`GuestMemory` based approach would not make such configuration possible.
+More details (including design
+limitations) in [the `packet`'s module-level documentation](src/packet.rs).
+
+A `VsockPacket` instance is created by parsing a descriptor chain from either
+the TX or the RX virtqueue. The `VsockPacket` API is also providing methods for
+creating/setting up packets directly from pointers to slices.
+It also offers setters and getters for each `virtio_vsock_hdr` field (e.g.
+*src_cid*, *dst_port*, *op*).
+
+### Usage
+
+The driver queues receive buffers on the RX virtqueue, and outgoing packets on
+the TX virtqueue. The device processes the RX virtqueue using
+`VsockPacket::from_rx_virtq_chain` and fills the buffers with data from the
+vsock backend.
+On the TX side, the device processes the TX queue using
+`VsockPacket::from_tx_virtq_chain`, packages the read buffers as vsock packets,
+and then sends them to the backend.
+
+### Examples
+
+Examples of usage can be found as documentation tests in
+[the packet module](src/packet.rs).
+
+## License
+
+This project is licensed under either of
+
+- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0
+- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
diff --git a/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..4241aa2
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,6 @@
+{
+ "run": true,
+ "dep-suffixes": {
+ "vm_memory": "_android"
+ }
+} \ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..682eefa
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,11 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+//
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+
+//! A crate that provides vsock device specific components as described
+//! by the virtio specification.
+
+#![deny(missing_docs)]
+
+/// Contains a vsock packet abstraction.
+pub mod packet;
diff --git a/src/packet.rs b/src/packet.rs
new file mode 100644
index 0000000..d493503
--- /dev/null
+++ b/src/packet.rs
@@ -0,0 +1,1397 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+//
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+
+//! Vsock packet abstraction.
+//!
+//! This module provides the following abstraction for parsing a vsock packet, and working with it:
+//!
+//! - [`VsockPacket`](struct.VsockPacket.html) which handles the parsing of the vsock packet from
+//! either a TX descriptor chain via
+//! [`VsockPacket::from_tx_virtq_chain`](struct.VsockPacket.html#method.from_tx_virtq_chain), or an
+//! RX descriptor chain via
+//! [`VsockPacket::from_rx_virtq_chain`](struct.VsockPacket.html#method.from_rx_virtq_chain).
+//! The virtio vsock packet is defined in the standard as having a header of type `virtio_vsock_hdr`
+//! and an optional `data` array of bytes. The methods mentioned above assume that both packet
+//! elements are on the same descriptor, or each of the packet elements occupies exactly one
+//! descriptor. For the usual drivers, this assumption stands,
+//! but in the future we might make the implementation more generic by removing any constraint
+//! regarding the number of descriptors that correspond to the header/data. The buffers associated
+//! to the TX virtio queue are device-readable, and the ones associated to the RX virtio queue are
+//! device-writable.
+///
+/// The `VsockPacket` abstraction is using vm-memory's `VolatileSlice` for representing the header
+/// and the data. `VolatileSlice` is a safe wrapper over a raw pointer, which also handles the dirty
+/// page tracking behind the scenes. A limitation of the current implementation is that it does not
+/// cover the scenario where the header or data buffer doesn't fit in a single `VolatileSlice`
+/// because the guest memory regions of the buffer are contiguous in the guest physical address
+/// space, but not in the host virtual one as well. If this becomes an use case, we can extend this
+/// solution to use an array of `VolatileSlice`s for the header and data.
+/// The `VsockPacket` abstraction is also storing a `virtio_vsock_hdr` instance (which is defined
+/// here as `PacketHeader`). This is needed so that we always access the same data that was read the
+/// first time from the descriptor chain. We avoid this way potential time-of-check time-of-use
+/// problems that may occur when reading later a header field from the underlying memory itself
+/// (i.e. from the header's `VolatileSlice` object).
+use std::fmt::{self, Display};
+use std::ops::Deref;
+
+use virtio_queue::DescriptorChain;
+use vm_memory::bitmap::{BitmapSlice, WithBitmapSlice};
+use vm_memory::{
+ Address, ByteValued, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion, Le16, Le32, Le64,
+ VolatileMemoryError, VolatileSlice,
+};
+
+/// Vsock packet parsing errors.
+#[derive(Debug)]
+pub enum Error {
+ /// Too few descriptors in a descriptor chain.
+ DescriptorChainTooShort,
+ /// Descriptor that was too short to use.
+ DescriptorLengthTooSmall,
+ /// Descriptor that was too long to use.
+ DescriptorLengthTooLong,
+ /// The slice for creating a header has an invalid length.
+ InvalidHeaderInputSize(usize),
+ /// The `len` header field value exceeds the maximum allowed data size.
+ InvalidHeaderLen(u32),
+ /// Invalid guest memory access.
+ InvalidMemoryAccess(GuestMemoryError),
+ /// Invalid volatile memory access.
+ InvalidVolatileAccess(VolatileMemoryError),
+ /// Read only descriptor that protocol says to write to.
+ UnexpectedReadOnlyDescriptor,
+ /// Write only descriptor that protocol says to read from.
+ UnexpectedWriteOnlyDescriptor,
+}
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::DescriptorChainTooShort => {
+ write!(f, "There are not enough descriptors in the chain.")
+ }
+ Error::DescriptorLengthTooSmall => write!(
+ f,
+ "The descriptor is pointing to a buffer that has a smaller length than expected."
+ ),
+ Error::DescriptorLengthTooLong => write!(
+ f,
+ "The descriptor is pointing to a buffer that has a longer length than expected."
+ ),
+ Error::InvalidHeaderInputSize(size) => {
+ write!(f, "Invalid header input size: {}", size)
+ }
+ Error::InvalidHeaderLen(size) => {
+ write!(f, "Invalid header `len` field value: {}", size)
+ }
+ Error::InvalidMemoryAccess(error) => {
+ write!(f, "Invalid guest memory access: {}", error)
+ }
+ Error::InvalidVolatileAccess(error) => {
+ write!(f, "Invalid volatile memory access: {}", error)
+ }
+ Error::UnexpectedReadOnlyDescriptor => {
+ write!(f, "Unexpected read-only descriptor.")
+ }
+ Error::UnexpectedWriteOnlyDescriptor => {
+ write!(f, "Unexpected write-only descriptor.")
+ }
+ }
+ }
+}
+
+#[repr(C, packed)]
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+/// The vsock packet header structure.
+pub struct PacketHeader {
+ src_cid: Le64,
+ dst_cid: Le64,
+ src_port: Le32,
+ dst_port: Le32,
+ len: Le32,
+ type_: Le16,
+ op: Le16,
+ flags: Le32,
+ buf_alloc: Le32,
+ fwd_cnt: Le32,
+}
+
+// SAFETY: This is safe because `PacketHeader` contains only wrappers over POD types
+// and all accesses through safe `vm-memory` API will validate any garbage that could
+// be included in there.
+unsafe impl ByteValued for PacketHeader {}
+//
+// This structure will occupy the buffer pointed to by the head of the descriptor chain. Below are
+// the offsets for each field, as well as the packed structure size.
+// Note that these offsets are only used privately by the `VsockPacket` struct, the public interface
+// consisting of getter and setter methods, for each struct field, that will also handle the correct
+// endianness.
+
+/// The size of the header structure (when packed).
+pub const PKT_HEADER_SIZE: usize = std::mem::size_of::<PacketHeader>();
+
+// Offsets of the header fields.
+const SRC_CID_OFFSET: usize = 0;
+const DST_CID_OFFSET: usize = 8;
+const SRC_PORT_OFFSET: usize = 16;
+const DST_PORT_OFFSET: usize = 20;
+const LEN_OFFSET: usize = 24;
+const TYPE_OFFSET: usize = 28;
+const OP_OFFSET: usize = 30;
+const FLAGS_OFFSET: usize = 32;
+const BUF_ALLOC_OFFSET: usize = 36;
+const FWD_CNT_OFFSET: usize = 40;
+
+/// Dedicated [`Result`](https://doc.rust-lang.org/std/result/) type.
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// The vsock packet, implemented as a wrapper over a virtio descriptor chain:
+/// - the chain head, holding the packet header;
+/// - an optional data/buffer descriptor, only present for data packets (for VSOCK_OP_RW requests).
+#[derive(Debug)]
+pub struct VsockPacket<'a, B: BitmapSlice> {
+ // When writing to the header slice, we are using the `write` method of `VolatileSlice`s Bytes
+ // implementation. Because that can only return an error if we pass an invalid offset, we can
+ // safely use `unwraps` in the setters below. If we switch to a type different than
+ // `VolatileSlice`, this assumption can no longer hold. We also must always make sure the
+ // `VsockPacket` API is creating headers with PKT_HEADER_SIZE size.
+ header_slice: VolatileSlice<'a, B>,
+ header: PacketHeader,
+ data_slice: Option<VolatileSlice<'a, B>>,
+}
+
+// This macro is intended to be used for setting a header field in both the `VolatileSlice` and the
+// `PacketHeader` structure from a packet. `$offset` should be a valid offset in the `header_slice`,
+// otherwise the macro will panic.
+macro_rules! set_header_field {
+ ($packet:ident, $field:ident, $offset:ident, $value:ident) => {
+ $packet.header.$field = $value.into();
+ $packet
+ .header_slice
+ .write(&$value.to_le_bytes(), $offset)
+ // This unwrap is safe only if `$offset` is a valid offset in the `header_slice`.
+ .unwrap();
+ };
+}
+
+impl<'a, B: BitmapSlice> VsockPacket<'a, B> {
+ /// Return a reference to the `header_slice` of the packet.
+ pub fn header_slice(&self) -> &VolatileSlice<'a, B> {
+ &self.header_slice
+ }
+
+ /// Return a reference to the `data_slice` of the packet.
+ pub fn data_slice(&self) -> Option<&VolatileSlice<'a, B>> {
+ self.data_slice.as_ref()
+ }
+
+ /// Write to the packet header from an input of raw bytes.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE;
+ /// # use virtio_queue::mock::MockSplitQueue;
+ /// # use virtio_queue::{Descriptor, Queue, QueueT};
+ /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE};
+ /// # use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
+ ///
+ /// const MAX_PKT_BUF_SIZE: u32 = 64 * 1024;
+ ///
+ /// # fn create_queue_with_chain(m: &GuestMemoryMmap) -> Queue {
+ /// # let vq = MockSplitQueue::new(m, 16);
+ /// # let mut q = vq.create_queue().unwrap();
+ /// #
+ /// # let v = vec![
+ /// # Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ /// # Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ /// # ];
+ /// # let mut chain = vq.build_desc_chain(&v);
+ /// # q
+ /// # }
+ /// let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
+ /// // Create a queue and populate it with a descriptor chain.
+ /// let mut queue = create_queue_with_chain(&mem);
+ ///
+ /// while let Some(mut head) = queue.pop_descriptor_chain(&mem) {
+ /// let mut pkt = VsockPacket::from_rx_virtq_chain(&mem, &mut head, MAX_PKT_BUF_SIZE).unwrap();
+ /// pkt.set_header_from_raw(&[0u8; PKT_HEADER_SIZE]).unwrap();
+ /// }
+ /// ```
+ pub fn set_header_from_raw(&mut self, bytes: &[u8]) -> Result<()> {
+ if bytes.len() != PKT_HEADER_SIZE {
+ return Err(Error::InvalidHeaderInputSize(bytes.len()));
+ }
+ self.header_slice
+ .write(bytes, 0)
+ .map_err(Error::InvalidVolatileAccess)?;
+ let header = self
+ .header_slice()
+ .read_obj::<PacketHeader>(0)
+ .map_err(Error::InvalidVolatileAccess)?;
+ self.header = header;
+ Ok(())
+ }
+
+ /// Return the `src_cid` of the header.
+ pub fn src_cid(&self) -> u64 {
+ self.header.src_cid.into()
+ }
+
+ /// Set the `src_cid` of the header.
+ pub fn set_src_cid(&mut self, cid: u64) -> &mut Self {
+ set_header_field!(self, src_cid, SRC_CID_OFFSET, cid);
+ self
+ }
+
+ /// Return the `dst_cid` of the header.
+ pub fn dst_cid(&self) -> u64 {
+ self.header.dst_cid.into()
+ }
+
+ /// Set the `dst_cid` of the header.
+ pub fn set_dst_cid(&mut self, cid: u64) -> &mut Self {
+ set_header_field!(self, dst_cid, DST_CID_OFFSET, cid);
+ self
+ }
+
+ /// Return the `src_port` of the header.
+ pub fn src_port(&self) -> u32 {
+ self.header.src_port.into()
+ }
+
+ /// Set the `src_port` of the header.
+ pub fn set_src_port(&mut self, port: u32) -> &mut Self {
+ set_header_field!(self, src_port, SRC_PORT_OFFSET, port);
+ self
+ }
+
+ /// Return the `dst_port` of the header.
+ pub fn dst_port(&self) -> u32 {
+ self.header.dst_port.into()
+ }
+
+ /// Set the `dst_port` of the header.
+ pub fn set_dst_port(&mut self, port: u32) -> &mut Self {
+ set_header_field!(self, dst_port, DST_PORT_OFFSET, port);
+ self
+ }
+
+ /// Return the `len` of the header.
+ pub fn len(&self) -> u32 {
+ self.header.len.into()
+ }
+
+ /// Returns whether the `len` field of the header is 0 or not.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Set the `len` of the header.
+ pub fn set_len(&mut self, len: u32) -> &mut Self {
+ set_header_field!(self, len, LEN_OFFSET, len);
+ self
+ }
+
+ /// Return the `type` of the header.
+ pub fn type_(&self) -> u16 {
+ self.header.type_.into()
+ }
+
+ /// Set the `type` of the header.
+ pub fn set_type(&mut self, type_: u16) -> &mut Self {
+ set_header_field!(self, type_, TYPE_OFFSET, type_);
+ self
+ }
+
+ /// Return the `op` of the header.
+ pub fn op(&self) -> u16 {
+ self.header.op.into()
+ }
+
+ /// Set the `op` of the header.
+ pub fn set_op(&mut self, op: u16) -> &mut Self {
+ set_header_field!(self, op, OP_OFFSET, op);
+ self
+ }
+
+ /// Return the `flags` of the header.
+ pub fn flags(&self) -> u32 {
+ self.header.flags.into()
+ }
+
+ /// Set the `flags` of the header.
+ pub fn set_flags(&mut self, flags: u32) -> &mut Self {
+ set_header_field!(self, flags, FLAGS_OFFSET, flags);
+ self
+ }
+
+ /// Set a specific flag of the header.
+ pub fn set_flag(&mut self, flag: u32) -> &mut Self {
+ self.set_flags(self.flags() | flag);
+ self
+ }
+
+ /// Return the `buf_alloc` of the header.
+ pub fn buf_alloc(&self) -> u32 {
+ self.header.buf_alloc.into()
+ }
+
+ /// Set the `buf_alloc` of the header.
+ pub fn set_buf_alloc(&mut self, buf_alloc: u32) -> &mut Self {
+ set_header_field!(self, buf_alloc, BUF_ALLOC_OFFSET, buf_alloc);
+ self
+ }
+
+ /// Return the `fwd_cnt` of the header.
+ pub fn fwd_cnt(&self) -> u32 {
+ self.header.fwd_cnt.into()
+ }
+
+ /// Set the `fwd_cnt` of the header.
+ pub fn set_fwd_cnt(&mut self, fwd_cnt: u32) -> &mut Self {
+ set_header_field!(self, fwd_cnt, FWD_CNT_OFFSET, fwd_cnt);
+ self
+ }
+
+ /// Create the packet wrapper from a TX chain.
+ ///
+ /// The chain head is expected to hold a valid packet header. A following packet data
+ /// descriptor can optionally end the chain.
+ ///
+ /// # Arguments
+ /// * `mem` - the `GuestMemory` object that can be used to access the queue buffers.
+ /// * `desc_chain` - the descriptor chain corresponding to a packet.
+ /// * `max_data_size` - the maximum size allowed for the packet payload, that was negotiated
+ /// between the device and the driver. Tracking issue for defining this
+ /// feature in virtio-spec
+ /// [here](https://github.com/oasis-tcs/virtio-spec/issues/140).
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use virtio_queue::mock::MockSplitQueue;
+ /// # use virtio_queue::{Descriptor, Queue, QueueT};
+ /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE};
+ /// # use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
+ ///
+ /// const MAX_PKT_BUF_SIZE: u32 = 64 * 1024;
+ /// const OP_RW: u16 = 5;
+ ///
+ /// # fn create_queue_with_chain(m: &GuestMemoryMmap) -> Queue {
+ /// # let vq = MockSplitQueue::new(m, 16);
+ /// # let mut q = vq.create_queue().unwrap();
+ /// #
+ /// # let v = vec![
+ /// # Descriptor::new(0x5_0000, 0x100, 0, 0),
+ /// # Descriptor::new(0x8_0000, 0x100, 0, 0),
+ /// # ];
+ /// # let mut chain = vq.build_desc_chain(&v);
+ /// # q
+ /// # }
+ /// let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
+ /// // Create a queue and populate it with a descriptor chain.
+ /// let mut queue = create_queue_with_chain(&mem);
+ ///
+ /// while let Some(mut head) = queue.pop_descriptor_chain(&mem) {
+ /// let pkt = match VsockPacket::from_tx_virtq_chain(&mem, &mut head, MAX_PKT_BUF_SIZE) {
+ /// Ok(pkt) => pkt,
+ /// Err(_e) => {
+ /// // Do some error handling.
+ /// queue.add_used(&mem, head.head_index(), 0);
+ /// continue;
+ /// }
+ /// };
+ /// // Here we would send the packet to the backend. Depending on the operation type, a
+ /// // different type of action will be done.
+ ///
+ /// // For example, if it's a RW packet, we will forward the packet payload to the backend.
+ /// if pkt.op() == OP_RW {
+ /// // Send the packet payload to the backend.
+ /// }
+ /// queue.add_used(&mem, head.head_index(), 0);
+ /// }
+ /// ```
+ pub fn from_tx_virtq_chain<M, T>(
+ mem: &'a M,
+ desc_chain: &mut DescriptorChain<T>,
+ max_data_size: u32,
+ ) -> Result<Self>
+ where
+ M: GuestMemory,
+ <<M as GuestMemory>::R as GuestMemoryRegion>::B: WithBitmapSlice<'a, S = B>,
+ T: Deref,
+ T::Target: GuestMemory,
+ {
+ let chain_head = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
+ // All TX buffers must be device-readable.
+ if chain_head.is_write_only() {
+ return Err(Error::UnexpectedWriteOnlyDescriptor);
+ }
+
+ // The packet header should fit inside the buffer corresponding to the head descriptor.
+ if (chain_head.len() as usize) < PKT_HEADER_SIZE {
+ return Err(Error::DescriptorLengthTooSmall);
+ }
+
+ let header_slice = mem
+ .get_slice(chain_head.addr(), PKT_HEADER_SIZE)
+ .map_err(Error::InvalidMemoryAccess)?;
+
+ let header = mem
+ .read_obj(chain_head.addr())
+ .map_err(Error::InvalidMemoryAccess)?;
+
+ let mut pkt = Self {
+ header_slice,
+ header,
+ data_slice: None,
+ };
+
+ // If the `len` field of the header is zero, then the packet doesn't have a `data` element.
+ if pkt.is_empty() {
+ return Ok(pkt);
+ }
+
+ // Reject packets that exceed the maximum allowed value for payload.
+ if pkt.len() > max_data_size {
+ return Err(Error::InvalidHeaderLen(pkt.len()));
+ }
+
+ // Starting from Linux 6.2 the virtio-vsock driver can use a single descriptor for both
+ // header and data.
+ let data_slice =
+ if !chain_head.has_next() && chain_head.len() - PKT_HEADER_SIZE as u32 >= pkt.len() {
+ mem.get_slice(
+ chain_head
+ .addr()
+ .checked_add(PKT_HEADER_SIZE as u64)
+ .ok_or(Error::DescriptorLengthTooSmall)?,
+ pkt.len() as usize,
+ )
+ .map_err(Error::InvalidMemoryAccess)?
+ } else {
+ if !chain_head.has_next() {
+ return Err(Error::DescriptorChainTooShort);
+ }
+
+ let data_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
+
+ if data_desc.is_write_only() {
+ return Err(Error::UnexpectedWriteOnlyDescriptor);
+ }
+
+ // The data buffer should be large enough to fit the size of the data, as described by
+ // the header descriptor.
+ if data_desc.len() < pkt.len() {
+ return Err(Error::DescriptorLengthTooSmall);
+ }
+
+ mem.get_slice(data_desc.addr(), pkt.len() as usize)
+ .map_err(Error::InvalidMemoryAccess)?
+ };
+
+ pkt.data_slice = Some(data_slice);
+ Ok(pkt)
+ }
+
+ /// Create the packet wrapper from an RX chain.
+ ///
+ /// There must be two descriptors in the chain, both writable: a header descriptor and a data
+ /// descriptor.
+ ///
+ /// # Arguments
+ /// * `mem` - the `GuestMemory` object that can be used to access the queue buffers.
+ /// * `desc_chain` - the descriptor chain corresponding to a packet.
+ /// * `max_data_size` - the maximum size allowed for the packet payload, that was negotiated
+ /// between the device and the driver. Tracking issue for defining this
+ /// feature in virtio-spec
+ /// [here](https://github.com/oasis-tcs/virtio-spec/issues/140).
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE;
+ /// # use virtio_queue::mock::MockSplitQueue;
+ /// # use virtio_queue::{Descriptor, Queue, QueueT};
+ /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE};
+ /// # use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
+ ///
+ /// # const MAX_PKT_BUF_SIZE: u32 = 64 * 1024;
+ /// # const SRC_CID: u64 = 1;
+ /// # const DST_CID: u64 = 2;
+ /// # const SRC_PORT: u32 = 3;
+ /// # const DST_PORT: u32 = 4;
+ /// # const LEN: u32 = 16;
+ /// # const TYPE_STREAM: u16 = 1;
+ /// # const OP_RW: u16 = 5;
+ /// # const FLAGS: u32 = 7;
+ /// # const FLAG: u32 = 8;
+ /// # const BUF_ALLOC: u32 = 256;
+ /// # const FWD_CNT: u32 = 9;
+ ///
+ /// # fn create_queue_with_chain(m: &GuestMemoryMmap) -> Queue {
+ /// # let vq = MockSplitQueue::new(m, 16);
+ /// # let mut q = vq.create_queue().unwrap();
+ /// #
+ /// # let v = vec![
+ /// # Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ /// # Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ /// # ];
+ /// # let mut chain = vq.build_desc_chain(&v);
+ /// # q
+ /// # }
+ /// let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
+ /// // Create a queue and populate it with a descriptor chain.
+ /// let mut queue = create_queue_with_chain(&mem);
+ ///
+ /// while let Some(mut head) = queue.pop_descriptor_chain(&mem) {
+ /// let used_len = match VsockPacket::from_rx_virtq_chain(&mem, &mut head, MAX_PKT_BUF_SIZE) {
+ /// Ok(mut pkt) => {
+ /// // Make sure the header is zeroed out first.
+ /// pkt.header_slice()
+ /// .write(&[0u8; PKT_HEADER_SIZE], 0)
+ /// .unwrap();
+ /// // Write data to the packet, using the setters.
+ /// pkt.set_src_cid(SRC_CID)
+ /// .set_dst_cid(DST_CID)
+ /// .set_src_port(SRC_PORT)
+ /// .set_dst_port(DST_PORT)
+ /// .set_type(TYPE_STREAM)
+ /// .set_buf_alloc(BUF_ALLOC)
+ /// .set_fwd_cnt(FWD_CNT);
+ /// // In this example, we are sending a RW packet.
+ /// pkt.data_slice()
+ /// .unwrap()
+ /// .write_slice(&[1u8; LEN as usize], 0);
+ /// pkt.set_op(OP_RW).set_len(LEN);
+ /// pkt.header_slice().len() as u32 + LEN
+ /// }
+ /// Err(_e) => {
+ /// // Do some error handling.
+ /// 0
+ /// }
+ /// };
+ /// queue.add_used(&mem, head.head_index(), used_len);
+ /// }
+ /// ```
+ pub fn from_rx_virtq_chain<M, T>(
+ mem: &'a M,
+ desc_chain: &mut DescriptorChain<T>,
+ max_data_size: u32,
+ ) -> Result<Self>
+ where
+ M: GuestMemory,
+ <<M as GuestMemory>::R as GuestMemoryRegion>::B: WithBitmapSlice<'a, S = B>,
+ T: Deref,
+ T::Target: GuestMemory,
+ {
+ let chain_head = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
+ // All RX buffers must be device-writable.
+ if !chain_head.is_write_only() {
+ return Err(Error::UnexpectedReadOnlyDescriptor);
+ }
+
+ // The packet header should fit inside the head descriptor.
+ if (chain_head.len() as usize) < PKT_HEADER_SIZE {
+ return Err(Error::DescriptorLengthTooSmall);
+ }
+
+ let header_slice = mem
+ .get_slice(chain_head.addr(), PKT_HEADER_SIZE)
+ .map_err(Error::InvalidMemoryAccess)?;
+
+ // Starting from Linux 6.2 the virtio-vsock driver can use a single descriptor for both
+ // header and data.
+ let data_slice = if !chain_head.has_next() && chain_head.len() as usize > PKT_HEADER_SIZE {
+ mem.get_slice(
+ chain_head
+ .addr()
+ .checked_add(PKT_HEADER_SIZE as u64)
+ .ok_or(Error::DescriptorLengthTooSmall)?,
+ chain_head.len() as usize - PKT_HEADER_SIZE,
+ )
+ .map_err(Error::InvalidMemoryAccess)?
+ } else {
+ if !chain_head.has_next() {
+ return Err(Error::DescriptorChainTooShort);
+ }
+
+ let data_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
+
+ if !data_desc.is_write_only() {
+ return Err(Error::UnexpectedReadOnlyDescriptor);
+ }
+
+ if data_desc.len() > max_data_size {
+ return Err(Error::DescriptorLengthTooLong);
+ }
+
+ mem.get_slice(data_desc.addr(), data_desc.len() as usize)
+ .map_err(Error::InvalidMemoryAccess)?
+ };
+
+ Ok(Self {
+ header_slice,
+ header: Default::default(),
+ data_slice: Some(data_slice),
+ })
+ }
+}
+
+impl<'a> VsockPacket<'a, ()> {
+ /// Create a packet based on one pointer for the header, and an optional one for data.
+ ///
+ /// # Safety
+ ///
+ /// To use this safely, the caller must guarantee that the memory pointed to by the `hdr` and
+ /// `data` slices is available for the duration of the lifetime of the new `VolatileSlice`. The
+ /// caller must also guarantee that all other users of the given chunk of memory are using
+ /// volatile accesses.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE};
+ ///
+ /// const LEN: usize = 16;
+ ///
+ /// let mut pkt_raw = [0u8; PKT_HEADER_SIZE + LEN];
+ /// let (hdr_raw, data_raw) = pkt_raw.split_at_mut(PKT_HEADER_SIZE);
+ /// // Safe because `hdr_raw` and `data_raw` live for as long as the scope of the current
+ /// // example.
+ /// let packet = unsafe { VsockPacket::new(hdr_raw, Some(data_raw)).unwrap() };
+ /// ```
+ pub unsafe fn new(header: &mut [u8], data: Option<&mut [u8]>) -> Result<VsockPacket<'a, ()>> {
+ if header.len() != PKT_HEADER_SIZE {
+ return Err(Error::InvalidHeaderInputSize(header.len()));
+ }
+ Ok(VsockPacket {
+ header_slice: VolatileSlice::new(header.as_mut_ptr(), PKT_HEADER_SIZE),
+ header: Default::default(),
+ data_slice: data.map(|data| VolatileSlice::new(data.as_mut_ptr(), data.len())),
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use vm_memory::{GuestAddress, GuestMemoryMmap};
+
+ use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE;
+ use virtio_queue::mock::MockSplitQueue;
+ use virtio_queue::Descriptor;
+
+ impl PartialEq for Error {
+ fn eq(&self, other: &Self) -> bool {
+ use self::Error::*;
+ match (self, other) {
+ (DescriptorChainTooShort, DescriptorChainTooShort) => true,
+ (DescriptorLengthTooSmall, DescriptorLengthTooSmall) => true,
+ (DescriptorLengthTooLong, DescriptorLengthTooLong) => true,
+ (InvalidHeaderInputSize(size), InvalidHeaderInputSize(other_size)) => {
+ size == other_size
+ }
+ (InvalidHeaderLen(size), InvalidHeaderLen(other_size)) => size == other_size,
+ (InvalidMemoryAccess(ref e), InvalidMemoryAccess(ref other_e)) => {
+ format!("{}", e).eq(&format!("{}", other_e))
+ }
+ (InvalidVolatileAccess(ref e), InvalidVolatileAccess(ref other_e)) => {
+ format!("{}", e).eq(&format!("{}", other_e))
+ }
+ (UnexpectedReadOnlyDescriptor, UnexpectedReadOnlyDescriptor) => true,
+ (UnexpectedWriteOnlyDescriptor, UnexpectedWriteOnlyDescriptor) => true,
+ _ => false,
+ }
+ }
+ }
+
+ // Random values to be used by the tests for the header fields.
+ const SRC_CID: u64 = 1;
+ const DST_CID: u64 = 2;
+ const SRC_PORT: u32 = 3;
+ const DST_PORT: u32 = 4;
+ const LEN: u32 = 16;
+ const TYPE: u16 = 5;
+ const OP: u16 = 6;
+ const FLAGS: u32 = 7;
+ const FLAG: u32 = 8;
+ const BUF_ALLOC: u32 = 256;
+ const FWD_CNT: u32 = 9;
+
+ const MAX_PKT_BUF_SIZE: u32 = 64 * 1024;
+
+ #[test]
+ fn test_from_rx_virtq_chain() {
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x1000_0000)]).unwrap();
+
+ // The `build_desc_chain` function will populate the `NEXT` related flags and field.
+ let v = vec![
+ // A device-readable packet header descriptor should be invalid.
+ Descriptor::new(0x10_0000, 0x100, 0, 0),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::UnexpectedReadOnlyDescriptor
+ );
+
+ let v = vec![
+ // A header length < PKT_HEADER_SIZE is invalid.
+ Descriptor::new(
+ 0x10_0000,
+ PKT_HEADER_SIZE as u32 - 1,
+ VRING_DESC_F_WRITE as u16,
+ 0,
+ ),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorLengthTooSmall
+ );
+
+ let v = vec![
+ Descriptor::new(
+ 0x10_0000,
+ PKT_HEADER_SIZE as u32,
+ VRING_DESC_F_WRITE as u16,
+ 0,
+ ),
+ Descriptor::new(
+ 0x20_0000,
+ MAX_PKT_BUF_SIZE + 1,
+ VRING_DESC_F_WRITE as u16,
+ 0,
+ ),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorLengthTooLong
+ );
+
+ let v = vec![
+ // The data descriptor should always be present on the RX path.
+ Descriptor::new(
+ 0x10_0000,
+ PKT_HEADER_SIZE as u32,
+ VRING_DESC_F_WRITE as u16,
+ 0,
+ ),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorChainTooShort
+ );
+
+ let v = vec![
+ Descriptor::new(0x10_0000, 0x100, 0, 0),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::UnexpectedReadOnlyDescriptor
+ );
+
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10_0004)]).unwrap();
+
+ let v = vec![
+ // The header doesn't fit entirely in the memory bounds.
+ Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidBackendAddress)
+ );
+
+ let v = vec![
+ // The header is outside the memory bounds.
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x30_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidGuestAddress(GuestAddress(
+ 0x20_0000
+ )))
+ );
+
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ // A device-readable packet data descriptor should be invalid.
+ Descriptor::new(0x8_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::UnexpectedReadOnlyDescriptor
+ );
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ // The data array doesn't fit entirely in the memory bounds.
+ Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidBackendAddress)
+ );
+
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ // The data array is outside the memory bounds.
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidGuestAddress(GuestAddress(
+ 0x20_0000
+ )))
+ );
+
+ // Let's also test a valid descriptor chain.
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let packet = VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ assert_eq!(packet.header, PacketHeader::default());
+ let header = packet.header_slice();
+ assert_eq!(
+ header.as_ptr(),
+ mem.get_host_address(GuestAddress(0x5_0000)).unwrap()
+ );
+ assert_eq!(header.len(), PKT_HEADER_SIZE);
+
+ let data = packet.data_slice().unwrap();
+ assert_eq!(
+ data.as_ptr(),
+ mem.get_host_address(GuestAddress(0x8_0000)).unwrap()
+ );
+ assert_eq!(data.len(), 0x100);
+
+ // If we try to get a vsock packet again, it fails because we already consumed all the
+ // descriptors from the chain.
+ assert_eq!(
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorChainTooShort
+ );
+
+ // Let's also test a valid descriptor chain, with both header and data on a single
+ // descriptor.
+ let v = vec![Descriptor::new(
+ 0x5_0000,
+ PKT_HEADER_SIZE as u32 + 0x100,
+ VRING_DESC_F_WRITE as u16,
+ 0,
+ )];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let packet = VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ assert_eq!(packet.header, PacketHeader::default());
+ let header = packet.header_slice();
+ assert_eq!(
+ header.as_ptr(),
+ mem.get_host_address(GuestAddress(0x5_0000)).unwrap()
+ );
+ assert_eq!(header.len(), PKT_HEADER_SIZE);
+
+ let data = packet.data_slice().unwrap();
+ assert_eq!(
+ data.as_ptr(),
+ mem.get_host_address(GuestAddress(0x5_0000 + PKT_HEADER_SIZE as u64))
+ .unwrap()
+ );
+ assert_eq!(data.len(), 0x100);
+ }
+
+ #[test]
+ fn test_from_tx_virtq_chain() {
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x1000_0000)]).unwrap();
+
+ // The `build_desc_chain` function will populate the `NEXT` related flags and field.
+ let v = vec![
+ // A device-writable packet header descriptor should be invalid.
+ Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x20_0000, 0x100, 0, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::UnexpectedWriteOnlyDescriptor
+ );
+
+ let v = vec![
+ // A header length < PKT_HEADER_SIZE is invalid.
+ Descriptor::new(0x10_0000, PKT_HEADER_SIZE as u32 - 1, 0, 0),
+ Descriptor::new(0x20_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorLengthTooSmall
+ );
+
+ // On the TX path, it is allowed to not have a data descriptor.
+ let v = vec![Descriptor::new(0x10_0000, PKT_HEADER_SIZE as u32, 0, 0)];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let header = PacketHeader {
+ src_cid: SRC_CID.into(),
+ dst_cid: DST_CID.into(),
+ src_port: SRC_PORT.into(),
+ dst_port: DST_PORT.into(),
+ len: 0.into(),
+ type_: 0.into(),
+ op: 0.into(),
+ flags: 0.into(),
+ buf_alloc: 0.into(),
+ fwd_cnt: 0.into(),
+ };
+ mem.write_obj(header, GuestAddress(0x10_0000)).unwrap();
+
+ let packet = VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ assert_eq!(packet.header, header);
+ let header_slice = packet.header_slice();
+ assert_eq!(
+ header_slice.as_ptr(),
+ mem.get_host_address(GuestAddress(0x10_0000)).unwrap()
+ );
+ assert_eq!(header_slice.len(), PKT_HEADER_SIZE);
+ assert!(packet.data_slice().is_none());
+
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10_0004)]).unwrap();
+
+ let v = vec![
+ // The header doesn't fit entirely in the memory bounds.
+ Descriptor::new(0x10_0000, 0x100, 0, 0),
+ Descriptor::new(0x20_0000, 0x100, 0, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidBackendAddress)
+ );
+
+ let v = vec![
+ // The header is outside the memory bounds.
+ Descriptor::new(0x20_0000, 0x100, 0, 0),
+ Descriptor::new(0x30_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidGuestAddress(GuestAddress(
+ 0x20_0000
+ )))
+ );
+
+ // Write some non-zero value to the `len` field of the header, which means there is also
+ // a data descriptor in the chain, first with a value that exceeds the maximum allowed one.
+ let header = PacketHeader {
+ src_cid: SRC_CID.into(),
+ dst_cid: DST_CID.into(),
+ src_port: SRC_PORT.into(),
+ dst_port: DST_PORT.into(),
+ len: (MAX_PKT_BUF_SIZE + 1).into(),
+ type_: 0.into(),
+ op: 0.into(),
+ flags: 0.into(),
+ buf_alloc: 0.into(),
+ fwd_cnt: 0.into(),
+ };
+ mem.write_obj(header, GuestAddress(0x5_0000)).unwrap();
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, 0, 0),
+ Descriptor::new(0x8_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidHeaderLen(MAX_PKT_BUF_SIZE + 1)
+ );
+
+ // Write some non-zero, valid value to the `len` field of the header.
+ let header = PacketHeader {
+ src_cid: SRC_CID.into(),
+ dst_cid: DST_CID.into(),
+ src_port: SRC_PORT.into(),
+ dst_port: DST_PORT.into(),
+ len: LEN.into(),
+ type_: 0.into(),
+ op: 0.into(),
+ flags: 0.into(),
+ buf_alloc: 0.into(),
+ fwd_cnt: 0.into(),
+ };
+ mem.write_obj(header, GuestAddress(0x5_0000)).unwrap();
+ let v = vec![
+ // The data descriptor is missing.
+ Descriptor::new(0x5_0000, PKT_HEADER_SIZE as u32, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorChainTooShort
+ );
+
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, 0, 0),
+ // The data array doesn't fit entirely in the memory bounds.
+ Descriptor::new(0x10_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidBackendAddress)
+ );
+
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, 0, 0),
+ // The data array is outside the memory bounds.
+ Descriptor::new(0x20_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::InvalidMemoryAccess(GuestMemoryError::InvalidGuestAddress(GuestAddress(
+ 0x20_0000
+ )))
+ );
+
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, 0, 0),
+ // A device-writable packet data descriptor should be invalid.
+ Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::UnexpectedWriteOnlyDescriptor
+ );
+
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, 0, 0),
+ // A data length < the length of data as described by the header.
+ Descriptor::new(0x8_0000, LEN - 1, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorLengthTooSmall
+ );
+
+ // Let's also test a valid descriptor chain, with both header and data.
+ let v = vec![
+ Descriptor::new(0x5_0000, 0x100, 0, 0),
+ Descriptor::new(0x8_0000, 0x100, 0, 0),
+ ];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let packet = VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ assert_eq!(packet.header, header);
+ let header_slice = packet.header_slice();
+ assert_eq!(
+ header_slice.as_ptr(),
+ mem.get_host_address(GuestAddress(0x5_0000)).unwrap()
+ );
+ assert_eq!(header_slice.len(), PKT_HEADER_SIZE);
+ // The `len` field of the header was set to 16.
+ assert_eq!(packet.len(), LEN);
+
+ let data = packet.data_slice().unwrap();
+ assert_eq!(
+ data.as_ptr(),
+ mem.get_host_address(GuestAddress(0x8_0000)).unwrap()
+ );
+ assert_eq!(data.len(), LEN as usize);
+
+ // If we try to get a vsock packet again, it fails because we already consumed all the
+ // descriptors from the chain.
+ assert_eq!(
+ VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap_err(),
+ Error::DescriptorChainTooShort
+ );
+
+ // Let's also test a valid descriptor chain, with both header and data on a single
+ // descriptor.
+ let v = vec![Descriptor::new(
+ 0x5_0000,
+ PKT_HEADER_SIZE as u32 + 0x100,
+ 0,
+ 0,
+ )];
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let packet = VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ assert_eq!(packet.header, header);
+ let header_slice = packet.header_slice();
+ assert_eq!(
+ header_slice.as_ptr(),
+ mem.get_host_address(GuestAddress(0x5_0000)).unwrap()
+ );
+ assert_eq!(header_slice.len(), PKT_HEADER_SIZE);
+ // The `len` field of the header was set to 16.
+ assert_eq!(packet.len(), LEN);
+
+ let data = packet.data_slice().unwrap();
+ assert_eq!(
+ data.as_ptr(),
+ mem.get_host_address(GuestAddress(0x5_0000 + PKT_HEADER_SIZE as u64))
+ .unwrap()
+ );
+ assert_eq!(data.len(), LEN as usize);
+ }
+
+ #[test]
+ fn test_header_set_get() {
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x30_0000)]).unwrap();
+ // The `build_desc_chain` function will populate the `NEXT` related flags and field.
+ let v = vec![
+ Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let mut packet =
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ packet
+ .set_src_cid(SRC_CID)
+ .set_dst_cid(DST_CID)
+ .set_src_port(SRC_PORT)
+ .set_dst_port(DST_PORT)
+ .set_len(LEN)
+ .set_type(TYPE)
+ .set_op(OP)
+ .set_flags(FLAGS)
+ .set_flag(FLAG)
+ .set_buf_alloc(BUF_ALLOC)
+ .set_fwd_cnt(FWD_CNT);
+
+ assert_eq!(packet.flags(), FLAGS | FLAG);
+ assert_eq!(packet.op(), OP);
+ assert_eq!(packet.type_(), TYPE);
+ assert_eq!(packet.dst_cid(), DST_CID);
+ assert_eq!(packet.dst_port(), DST_PORT);
+ assert_eq!(packet.src_cid(), SRC_CID);
+ assert_eq!(packet.src_port(), SRC_PORT);
+ assert_eq!(packet.fwd_cnt(), FWD_CNT);
+ assert_eq!(packet.len(), LEN);
+ assert_eq!(packet.buf_alloc(), BUF_ALLOC);
+
+ let expected_header = PacketHeader {
+ src_cid: SRC_CID.into(),
+ dst_cid: DST_CID.into(),
+ src_port: SRC_PORT.into(),
+ dst_port: DST_PORT.into(),
+ len: LEN.into(),
+ type_: TYPE.into(),
+ op: OP.into(),
+ flags: (FLAGS | FLAG).into(),
+ buf_alloc: BUF_ALLOC.into(),
+ fwd_cnt: FWD_CNT.into(),
+ };
+
+ assert_eq!(packet.header, expected_header);
+ assert_eq!(
+ u64::from_le(
+ packet
+ .header_slice()
+ .read_obj::<u64>(SRC_CID_OFFSET)
+ .unwrap()
+ ),
+ SRC_CID
+ );
+ assert_eq!(
+ u64::from_le(
+ packet
+ .header_slice()
+ .read_obj::<u64>(DST_CID_OFFSET)
+ .unwrap()
+ ),
+ DST_CID
+ );
+ assert_eq!(
+ u32::from_le(
+ packet
+ .header_slice()
+ .read_obj::<u32>(SRC_PORT_OFFSET)
+ .unwrap()
+ ),
+ SRC_PORT
+ );
+ assert_eq!(
+ u32::from_le(
+ packet
+ .header_slice()
+ .read_obj::<u32>(DST_PORT_OFFSET)
+ .unwrap()
+ ),
+ DST_PORT,
+ );
+ assert_eq!(
+ u32::from_le(packet.header_slice().read_obj::<u32>(LEN_OFFSET).unwrap()),
+ LEN
+ );
+ assert_eq!(
+ u16::from_le(packet.header_slice().read_obj::<u16>(TYPE_OFFSET).unwrap()),
+ TYPE
+ );
+ assert_eq!(
+ u16::from_le(packet.header_slice().read_obj::<u16>(OP_OFFSET).unwrap()),
+ OP
+ );
+ assert_eq!(
+ u32::from_le(packet.header_slice().read_obj::<u32>(FLAGS_OFFSET).unwrap()),
+ FLAGS | FLAG
+ );
+ assert_eq!(
+ u32::from_le(
+ packet
+ .header_slice()
+ .read_obj::<u32>(BUF_ALLOC_OFFSET)
+ .unwrap()
+ ),
+ BUF_ALLOC
+ );
+ assert_eq!(
+ u32::from_le(
+ packet
+ .header_slice()
+ .read_obj::<u32>(FWD_CNT_OFFSET)
+ .unwrap()
+ ),
+ FWD_CNT
+ );
+ }
+
+ #[test]
+ fn test_set_header_from_raw() {
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x30_0000)]).unwrap();
+ // The `build_desc_chain` function will populate the `NEXT` related flags and field.
+ let v = vec![
+ Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let mut packet =
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+
+ let header = PacketHeader {
+ src_cid: SRC_CID.into(),
+ dst_cid: DST_CID.into(),
+ src_port: SRC_PORT.into(),
+ dst_port: DST_PORT.into(),
+ len: LEN.into(),
+ type_: TYPE.into(),
+ op: OP.into(),
+ flags: (FLAGS | FLAG).into(),
+ buf_alloc: BUF_ALLOC.into(),
+ fwd_cnt: FWD_CNT.into(),
+ };
+
+ // SAFETY: created from an existing packet header.
+ let slice = unsafe {
+ std::slice::from_raw_parts(
+ (&header as *const PacketHeader) as *const u8,
+ std::mem::size_of::<PacketHeader>(),
+ )
+ };
+ assert_eq!(packet.header, PacketHeader::default());
+ packet.set_header_from_raw(slice).unwrap();
+ assert_eq!(packet.header, header);
+ let header_from_slice: PacketHeader = packet.header_slice().read_obj(0).unwrap();
+ assert_eq!(header_from_slice, header);
+
+ let invalid_slice = [0; PKT_HEADER_SIZE - 1];
+ assert_eq!(
+ packet.set_header_from_raw(&invalid_slice).unwrap_err(),
+ Error::InvalidHeaderInputSize(PKT_HEADER_SIZE - 1)
+ );
+ }
+
+ #[test]
+ fn test_packet_new() {
+ let mut pkt_raw = [0u8; PKT_HEADER_SIZE + LEN as usize];
+ let (hdr_raw, data_raw) = pkt_raw.split_at_mut(PKT_HEADER_SIZE);
+ // SAFETY: safe because ``hdr_raw` and `data_raw` live for as long as
+ // the scope of the current test.
+ let packet = unsafe { VsockPacket::new(hdr_raw, Some(data_raw)).unwrap() };
+ assert_eq!(packet.header_slice.as_ptr(), hdr_raw.as_mut_ptr());
+ assert_eq!(packet.header_slice.len(), PKT_HEADER_SIZE);
+ assert_eq!(packet.header, PacketHeader::default());
+ assert_eq!(packet.data_slice.unwrap().as_ptr(), data_raw.as_mut_ptr());
+ assert_eq!(packet.data_slice.unwrap().len(), LEN as usize);
+
+ // SAFETY: Safe because ``hdr_raw` and `data_raw` live as long as the
+ // scope of the current test.
+ let packet = unsafe { VsockPacket::new(hdr_raw, None).unwrap() };
+ assert_eq!(packet.header_slice.as_ptr(), hdr_raw.as_mut_ptr());
+ assert_eq!(packet.header, PacketHeader::default());
+ assert!(packet.data_slice.is_none());
+
+ let mut hdr_raw = [0u8; PKT_HEADER_SIZE - 1];
+ assert_eq!(
+ // SAFETY: Safe because ``hdr_raw` lives for as long as the scope of the current test.
+ unsafe { VsockPacket::new(&mut hdr_raw, None).unwrap_err() },
+ Error::InvalidHeaderInputSize(PKT_HEADER_SIZE - 1)
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_set_header_field_with_invalid_offset() {
+ const INVALID_OFFSET: usize = 50;
+
+ impl<'a, B: BitmapSlice> VsockPacket<'a, B> {
+ /// Set the `src_cid` of the header, but use an invalid offset for that.
+ pub fn set_src_cid_invalid(&mut self, cid: u64) -> &mut Self {
+ set_header_field!(self, src_cid, INVALID_OFFSET, cid);
+ self
+ }
+ }
+
+ let mem: GuestMemoryMmap =
+ GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x30_0000)]).unwrap();
+ // The `build_desc_chain` function will populate the `NEXT` related flags and field.
+ let v = vec![
+ Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0),
+ ];
+ let queue = MockSplitQueue::new(&mem, 16);
+ let mut chain = queue.build_desc_chain(&v).unwrap();
+
+ let mut packet =
+ VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap();
+ packet.set_src_cid_invalid(SRC_CID);
+ }
+}