diff options
author | Jeongik Cha <jeongik@google.com> | 2023-09-14 16:36:32 +0900 |
---|---|---|
committer | Jeongik Cha <jeongik@google.com> | 2023-09-27 03:09:33 +0900 |
commit | f364c156ca503f4e760afedf578ac6e9970f0c09 (patch) | |
tree | 79afbdd470ba00de781e84f642644d02662b849b | |
parent | 27910099dff0d15e4321d6c1515219e33e504f0a (diff) | |
download | virtio-vsock-f364c156ca503f4e760afedf578ac6e9970f0c09.tar.gz |
Import virtio-vsock
Bug: 277909042
Test: build
Change-Id: I7f2fbef54fe88be7792f028b3b2017a76ef88442
-rw-r--r-- | .cargo_vcs_info.json | 6 | ||||
-rw-r--r-- | Android.bp | 18 | ||||
-rw-r--r-- | CHANGELOG.md | 35 | ||||
-rw-r--r-- | Cargo.toml | 48 | ||||
-rw-r--r-- | Cargo.toml.orig | 20 | ||||
-rw-r--r-- | LICENSE | 232 | ||||
-rw-r--r-- | LICENSE-BSD-3-Clause | 27 | ||||
-rw-r--r-- | LICENSE_APACHE | 202 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | MODULE_LICENSE_BSD | 0 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | README.md | 63 | ||||
-rw-r--r-- | cargo2android.json | 6 | ||||
-rw-r--r-- | src/lib.rs | 11 | ||||
-rw-r--r-- | src/packet.rs | 1397 |
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"] } @@ -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 @@ -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); + } +} |