pnet_datalink-0.35.0/.cargo_vcs_info.json0000644000000001530000000000100137350ustar { "git": { "sha1": "97ece70e2f87744f2ab47b4177c3888289f89a8f" }, "path_in_vcs": "pnet_datalink" }pnet_datalink-0.35.0/Cargo.toml0000644000000030740000000000100117400ustar # 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 = "pnet_datalink" version = "0.35.0" authors = [ "Robert Clipsham ", "Linus Färnstrand ", ] description = "Cross-platform, datalink layer networking." homepage = "https://github.com/libpnet/libpnet" readme = "README.md" keywords = [ "networking", "datalink", "ethernet", "raw", ] categories = ["network-programming"] license = "MIT OR Apache-2.0" repository = "https://github.com/libpnet/libpnet" [package.metadata.docs.rs] features = ["serde"] [dependencies.ipnetwork] version = "0.20.0" [dependencies.libc] version = "0.2.147" [dependencies.netmap_sys] version = "0.1.4" features = ["netmap_with_libs"] optional = true [dependencies.pcap] version = "1.1.0" optional = true [dependencies.pnet_base] version = "0.35.0" default-features = false [dependencies.pnet_sys] version = "0.35.0" [dependencies.serde] version = "1.0.171" features = ["derive"] optional = true default-features = false [features] default = ["std"] netmap = [] std = ["pnet_base/std"] [target."cfg(windows)".dependencies.winapi] version = "0.3.9" pnet_datalink-0.35.0/Cargo.toml.orig000064400000000000000000000022011046102023000154100ustar 00000000000000[package] name = "pnet_datalink" version = "0.35.0" authors = ["Robert Clipsham ", "Linus Färnstrand "] license = "MIT OR Apache-2.0" homepage = "https://github.com/libpnet/libpnet" repository = "https://github.com/libpnet/libpnet" description = "Cross-platform, datalink layer networking." readme = "../README.md" keywords = ["networking", "datalink", "ethernet", "raw"] categories = ["network-programming"] edition = "2021" [features] netmap = [] std = ["pnet_base/std"] default = ["std"] [dependencies] libc = "0.2.147" ipnetwork = "0.20.0" pnet_base = { path = "../pnet_base", version = "0.35.0", default-features = false } pnet_sys = { path = "../pnet_sys", version = "0.35.0" } pcap = { version = "1.1.0", optional = true } netmap_sys = { version = "0.1.4", optional = true, features = ["netmap_with_libs"] } serde = { version = "1.0.171", optional = true, default-features = false, features = [ "derive" ] } [target.'cfg(windows)'.dependencies] winapi = "0.3.9" [package.metadata.docs.rs] # Enable the serde feature when generating docs on docs.rs, so the traits are visible features = ["serde"] pnet_datalink-0.35.0/LICENSE-APACHE000064400000000000000000000251371046102023000144620ustar 00000000000000 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. pnet_datalink-0.35.0/LICENSE-MIT000064400000000000000000000020501046102023000141570ustar 00000000000000Copyright (c) 2014-2016 Robert Clipsham Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pnet_datalink-0.35.0/README.md000064400000000000000000000101641046102023000140070ustar 00000000000000# libpnet [![Crates.io](https://img.shields.io/crates/v/pnet.svg)](https://crates.io/crates/pnet) ![License](https://img.shields.io/crates/l/pnet.svg) [![Documentation](https://docs.rs/pnet/badge.svg)](https://docs.rs/pnet/) Build Status: [![Build Status](https://github.com/libpnet/libpnet/actions/workflows/ci.yml/badge.svg)](https://github.com/libpnet/libpnet/actions/workflows/ci.yml) Discussion and support: * Live chat on IRC - [#libpnet on irc.libera.chat](https://kiwiirc.com/nextclient/irc.libera.chat/libpnet?nick=pnet-user42) * [GitHub Discussions](https://github.com/libpnet/libpnet/discussions) `libpnet` provides a cross-platform API for low level networking using Rust. There are four key components: * The `packet` module, allowing safe construction and manipulation of packets; * The `pnet_macros` crate, providing infrastructure for the packet module; * The `transport` module, which allows implementation of transport protocols; * The `datalink` module, which allows sending and receiving data link packets directly. ## Why? There are lots of reasons to use low level networking, and many more to do it using Rust. A few are outlined here: ### Developing Transport Protocols There are usually two ways to go about developing a new transport layer protocol: * Write it in a scripting language such as Python; * Write it using C. The former is great for trying out new ideas and rapid prototyping, however not so great as a real-world implementation. While you can usually get reasonable performance out of these implementations, they're generally significantly slower than an implementation in C, and not suitable for any "heavy lifting". The next option is to write it in C - this will give you great performance, but comes with a number of other issues: * Lack of memory safety - this is a huge source of security vulnerabilities and other bugs in C-based network stacks. It is far too easy to forget a bounds check or use a pointer after it is freed. * Lack of thread safety - you have to be very careful to make sure the correct locks are used, and used correctly. * Lack of high level abstractions - part of the appeal of scripting languages such as Python is the higher level of abstraction which enables simpler APIs and ease of programming. Using `libpnet` and Rust, you get the best of both worlds. The higher level abstractions, memory and thread safety, alongside the performance of C. ### Network Utilities Many networking utilities such as ping and traceroute rely on being able to manipulate network and transport headers, which isn't possible with standard networking stacks such as those provided by `std::io::net`. ### Data Link Layer It can be useful to work directly at the data link layer, to see packets as they are "on the wire". There are lots of uses for this, including network diagnostics, packet capture and traffic shaping. ## Documentation API documentation for the latest build can be found here: https://docs.rs/pnet/ ## Usage To use `libpnet` in your project, add the following to your Cargo.toml: ``` [dependencies.pnet] version = "0.35.0" ``` `libpnet` should work with the latest stable version of Rust. When running the test suite, there are a number of networking tests which will likely fail - the easiest way to workaround this is to run `cargo test` as a root or administrative user. This can often be avoided, however it is more involved. ### Windows There are three requirements for building on Windows: * You must use a version of Rust which uses the MSVC toolchain * You must have [WinPcap](https://www.winpcap.org/) or [npcap](https://nmap.org/npcap/) installed (tested with version WinPcap 4.1.3) (If using npcap, make sure to install with the "Install Npcap in WinPcap API-compatible Mode") * You must place `Packet.lib` from the [WinPcap Developers pack](https://www.winpcap.org/devel.htm) in a directory named `lib`, in the root of this repository. Alternatively, you can use any of the locations listed in the `%LIB%`/`$Env:LIB` environment variables. For the 64 bit toolchain it is in `WpdPack/Lib/x64/Packet.lib`, for the 32 bit toolchain, it is in `WpdPack/Lib/Packet.lib`. pnet_datalink-0.35.0/src/bindings/bpf.rs000064400000000000000000000114021046102023000162250ustar 00000000000000// Copyright (c) 2014 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(dead_code)] extern crate libc; use pnet_sys; pub const AF_LINK: libc::c_int = 18; const IF_NAMESIZE: usize = 16; const IFNAMSIZ: usize = IF_NAMESIZE; const IOC_IN: libc::c_ulong = 0x80000000; const IOC_OUT: libc::c_ulong = 0x40000000; const IOC_INOUT: libc::c_ulong = IOC_IN | IOC_OUT; const IOCPARM_SHIFT: libc::c_ulong = 13; const IOCPARM_MASK: libc::c_ulong = (1 << (IOCPARM_SHIFT as usize)) - 1; const SIZEOF_TIMEVAL: libc::c_ulong = 16; const SIZEOF_IFREQ: libc::c_ulong = 32; const SIZEOF_C_UINT: libc::c_ulong = 4; #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris" ))] const SIZEOF_C_LONG: libc::c_int = 8; pub const BIOCSETIF: libc::c_ulong = IOC_IN | ((SIZEOF_IFREQ & IOCPARM_MASK) << 16usize) | (('B' as libc::c_ulong) << 8usize) | 108; pub const BIOCIMMEDIATE: libc::c_ulong = IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 112; pub const BIOCGBLEN: libc::c_ulong = IOC_OUT | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 102; pub const BIOCGDLT: libc::c_ulong = IOC_OUT | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 106; pub const BIOCSBLEN: libc::c_ulong = IOC_INOUT | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 102; pub const BIOCSHDRCMPLT: libc::c_ulong = IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 117; pub const BIOCSRTIMEOUT: libc::c_ulong = IOC_IN | ((SIZEOF_TIMEVAL & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 109; #[cfg(any(target_os = "freebsd", target_os = "illumos", target_os = "solaris"))] pub const BIOCFEEDBACK: libc::c_ulong = IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 124; #[cfg(target_os = "netbsd")] pub const BIOCFEEDBACK: libc::c_ulong = IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 125; // NOTE Could use BIOCSSEESENT on OS X, though set to 1 by default anyway pub const DLT_NULL: libc::c_uint = 0; #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris" ))] const BPF_ALIGNMENT: libc::c_int = SIZEOF_C_LONG; #[cfg(any( target_os = "openbsd", target_os = "macos", target_os = "ios", target_os = "tvos", windows ))] const BPF_ALIGNMENT: libc::c_int = 4; pub fn BPF_WORDALIGN(x: isize) -> isize { let bpf_alignment = BPF_ALIGNMENT as isize; (x + (bpf_alignment - 1)) & !(bpf_alignment - 1) } // See /usr/include/net/if.h #[repr(C)] pub struct ifreq { pub ifr_name: [libc::c_char; IFNAMSIZ], pub ifru_addr: pnet_sys::SockAddr, // NOTE Should be a union } // See /usr/include/net/if_dl.h // sdl_data does not match if_dl.h on OS X, since the size of 12 is a minimum. // Will be unsafe // when sdl_nlen > 40. #[cfg(any( target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "macos", target_os = "ios", target_os = "tvos" ))] #[repr(C)] pub struct sockaddr_dl { pub sdl_len: libc::c_uchar, pub sdl_family: libc::c_uchar, pub sdl_index: libc::c_ushort, pub sdl_type: libc::c_uchar, pub sdl_nlen: libc::c_uchar, pub sdl_alen: libc::c_uchar, pub sdl_slen: libc::c_uchar, pub sdl_data: [libc::c_char; 46], } // See man 4 bpf or /usr/include/net/bpf.h [windows: or Common/Packet32.h] #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", all( any(target_os = "macos", target_os = "ios", target_os = "tvos"), target_pointer_width = "32" ), windows ))] #[repr(C)] pub struct bpf_hdr { pub bh_tstamp: libc::timeval, pub bh_caplen: u32, pub bh_datalen: u32, pub bh_hdrlen: libc::c_ushort, } #[repr(C)] pub struct timeval32 { pub tv_sec: i32, pub tv_usec: i32, } #[cfg(any( target_os = "openbsd", all( any(target_os = "macos", target_os = "ios", target_os = "tvos"), target_pointer_width = "64" ) ))] #[repr(C)] pub struct bpf_hdr { pub bh_tstamp: timeval32, pub bh_caplen: u32, pub bh_datalen: u32, pub bh_hdrlen: libc::c_ushort, } #[cfg(not(windows))] extern "C" { pub fn ioctl(d: libc::c_int, request: libc::c_ulong, ...) -> libc::c_int; } pnet_datalink-0.35.0/src/bindings/linux.rs000064400000000000000000000026311046102023000166210ustar 00000000000000// Copyright (c) 2014 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(non_camel_case_types)] #![allow(non_snake_case)] extern crate libc; pub const SOL_PACKET: libc::c_int = 263; pub const PACKET_ADD_MEMBERSHIP: libc::c_int = 1; pub const PACKET_MR_PROMISC: libc::c_int = 1; pub const PACKET_FANOUT: libc::c_int = 18; pub const PACKET_FANOUT_HASH: libc::c_int = 0; pub const PACKET_FANOUT_LB: libc::c_int = 1; pub const PACKET_FANOUT_CPU: libc::c_int = 2; pub const PACKET_FANOUT_ROLLOVER: libc::c_int = 3; pub const PACKET_FANOUT_RND: libc::c_int = 4; pub const PACKET_FANOUT_QM: libc::c_int = 5; pub const PACKET_FANOUT_CBPF: libc::c_int = 6; pub const PACKET_FANOUT_EBPF: libc::c_int = 7; pub const PACKET_FANOUT_FLAG_ROLLOVER: libc::c_uint = 0x1000; #[allow(dead_code)] // following flag is unused yet pub const PACKET_FANOUT_FLAG_UNIQUEID: libc::c_uint = 0x2000; pub const PACKET_FANOUT_FLAG_DEFRAG: libc::c_uint = 0x8000; // man 7 packet #[repr(C)] pub struct packet_mreq { pub mr_ifindex: libc::c_int, pub mr_type: libc::c_ushort, pub mr_alen: libc::c_ushort, pub mr_address: [libc::c_uchar; 8], } pnet_datalink-0.35.0/src/bindings/mod.rs000064400000000000000000000013221046102023000162350ustar 00000000000000// Copyright (c) 2014 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[cfg(any( target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "macos", target_os = "ios", target_os = "tvos", windows ))] pub mod bpf; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod linux; #[cfg(windows)] pub mod winpcap; pnet_datalink-0.35.0/src/bindings/winpcap.rs000064400000000000000000000267511046102023000171340ustar 00000000000000// Copyright (c) 2014 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(dead_code)] extern crate winapi; use self::winapi::ctypes; use self::winapi::shared::{guiddef, minwindef}; use self::winapi::um::{minwinbase, winnt}; use pnet_sys; #[repr(C)] pub struct _ADAPTER; pub type ADAPTER = _ADAPTER; pub type LPADAPTER = *mut _ADAPTER; #[repr(C)] pub struct _PACKET { pub hEvent: winnt::HANDLE, pub OverLapped: minwinbase::OVERLAPPED, pub Buffer: PVOID, pub Length: UINT, pub ulBytesReceived: minwindef::DWORD, pub bIoComplete: winnt::BOOLEAN, } pub type PACKET = _PACKET; pub type LPPACKET = *mut _PACKET; pub type TCHAR = ctypes::c_char; pub type PTSTR = *mut TCHAR; pub type PVOID = *mut ctypes::c_void; pub type PCHAR = *mut winnt::CHAR; pub type PWCHAR = *mut winnt::WCHAR; pub type UINT = ctypes::c_uint; pub type ULONG = ctypes::c_ulong; pub type PULONG = *mut ULONG; pub type ULONG64 = u64; pub type UINT32 = u32; pub type UINT8 = u8; pub type INT = i32; const MAX_ADAPTER_DESCRIPTION_LENGTH: usize = 128; const MAX_ADAPTER_NAME_LENGTH: usize = 256; const MAX_ADAPTER_ADDRESS_LENGTH: usize = 8; // from ntddndis.h pub const NDIS_PACKET_TYPE_PROMISCUOUS: ULONG = 0x00000020; // from IPTypes.h #[repr(C)] pub struct _IP_ADDRESS_STRING { pub String: [ctypes::c_char; 4 * 4], } pub type IP_ADDRESS_STRING = _IP_ADDRESS_STRING; pub type PIP_ADDRESS_STRING = *mut _IP_ADDRESS_STRING; pub type IP_MASK_STRING = _IP_ADDRESS_STRING; pub type PIP_MASK_STRING = *mut _IP_ADDRESS_STRING; #[repr(C)] pub struct _IP_ADDR_STRING { pub Next: *mut _IP_ADDR_STRING, pub IpAddress: IP_ADDRESS_STRING, pub IpMask: IP_MASK_STRING, pub Context: minwindef::DWORD, } pub type IP_ADDR_STRING = _IP_ADDR_STRING; pub type PIP_ADDR_STRING = *mut _IP_ADDR_STRING; #[repr(C)] pub struct _IP_ADAPTER_INFO { pub Next: *mut _IP_ADAPTER_INFO, pub ComboIndex: minwindef::DWORD, pub AdapterName: [ctypes::c_char; MAX_ADAPTER_NAME_LENGTH + 4], pub Description: [ctypes::c_char; MAX_ADAPTER_DESCRIPTION_LENGTH + 4], pub AddressLength: UINT, pub Address: [minwindef::BYTE; MAX_ADAPTER_ADDRESS_LENGTH], pub Index: minwindef::DWORD, pub Type: UINT, pub DhcpEnabled: UINT, pub CurrentIpAddress: PIP_ADDR_STRING, pub IpAddressList: IP_ADDR_STRING, pub GatewayList: IP_ADDR_STRING, pub DhcpServer: IP_ADDR_STRING, pub HaveWins: minwindef::BOOL, pub PrimaryWinsServer: IP_ADDR_STRING, pub SecondaryWinsServer: IP_ADDR_STRING, pub LeaseObtained: libc::time_t, pub LeaseExpires: libc::time_t, } pub type IP_ADAPTER_INFO = _IP_ADAPTER_INFO; pub type PIP_ADAPTER_INFO = *mut _IP_ADAPTER_INFO; const MAX_DHCPV6_DUID_LENGTH: usize = 130; const MAX_DNS_SUFFIX_STRING_LENGTH: usize = 256; pub type LPSOCKADDR = *mut pnet_sys::SockAddr; #[repr(C)] pub struct _SOCKET_ADDRESS { pub lpSockaddr: LPSOCKADDR, pub iSockaddrLength: INT, } pub type SOCKET_ADDRESS = _SOCKET_ADDRESS; pub type PSOCKET_ADDRESS = *mut _SOCKET_ADDRESS; #[repr(C)] pub enum IP_PREFIX_ORIGIN { IpPrefixOriginOther = 0, IpPrefixOriginManual, IpPrefixOriginWellKnown, IpPrefixOriginDhcp, IpPrefixOriginRouterAdvertisement, IpPrefixOriginUnchanged = 16, } #[repr(C)] pub enum IP_SUFFIX_ORIGIN { IpSuffixOriginOther = 0, IpSuffixOriginManual, IpSuffixOriginWellKnown, IpSuffixOriginDhcp, IpSuffixOriginLinkLayerAddress, IpSuffixOriginRandom, IpSuffixOriginUnchanged = 16, } #[repr(C)] pub enum IP_DAD_STATE { IpDadStateInvalid = 0, IpDadStateTentative, IpDadStateDuplicate, IpDadStateDeprecated, IpDadStatePreferred, } #[repr(C)] pub enum IF_OPER_STATUS { IfOperStatusUp = 1, IfOperStatusDown, IfOperStatusTesting, IfOperStatusUnknown, IfOperStatusDormant, IfOperStatusNotPresent, IfOperStatusLowerLayerDown, } #[repr(C)] pub struct _IP_ADAPTER_UNICAST_ADDRESS { pub Length: ULONG, pub Flags: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_UNICAST_ADDRESS, pub Address: SOCKET_ADDRESS, pub PrefixOrigin: IP_PREFIX_ORIGIN, pub SuffixOrigin: IP_SUFFIX_ORIGIN, pub DadState: IP_DAD_STATE, pub ValidLifetime: ULONG, pub PreferredLifetime: ULONG, pub LeaseLifetime: ULONG, pub OnLinkPrefixLength: UINT8, } pub type IP_ADAPTER_UNICAST_ADDRESS = _IP_ADAPTER_UNICAST_ADDRESS; pub type PIP_ADAPTER_UNICAST_ADDRESS = *mut _IP_ADAPTER_UNICAST_ADDRESS; #[repr(C)] pub struct _IP_ADAPTER_ANYCAST_ADDRESS { pub Length: ULONG, pub Flags: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_ANYCAST_ADDRESS, pub Address: SOCKET_ADDRESS, } pub type IP_ADAPTER_ANYCAST_ADDRESS = _IP_ADAPTER_ANYCAST_ADDRESS; pub type PIP_ADAPTER_ANYCAST_ADDRESS = *mut _IP_ADAPTER_ANYCAST_ADDRESS; #[repr(C)] pub struct _IP_ADAPTER_MULTICAST_ADDRESS { pub Length: ULONG, pub Flags: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_MULTICAST_ADDRESS, pub Address: SOCKET_ADDRESS, } pub type IP_ADAPTER_MULTICAST_ADDRESS = _IP_ADAPTER_MULTICAST_ADDRESS; pub type PIP_ADAPTER_MULTICAST_ADDRESS = *mut _IP_ADAPTER_MULTICAST_ADDRESS; #[repr(C)] pub struct _IP_ADAPTER_DNS_SERVER_ADDRESS { pub Length: ULONG, pub Flags: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_DNS_SERVER_ADDRESS, pub Address: SOCKET_ADDRESS, } pub type IP_ADAPTER_DNS_SERVER_ADDRESS = _IP_ADAPTER_DNS_SERVER_ADDRESS; pub type PIP_ADAPTER_DNS_SERVER_ADDRESS = *mut _IP_ADAPTER_DNS_SERVER_ADDRESS; #[repr(C)] pub struct _IP_ADAPTER_PREFIX { pub Length: ULONG, pub Flags: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_PREFIX, pub Address: SOCKET_ADDRESS, pub PrefixLength: ULONG, } pub type IP_ADAPTER_PREFIX = _IP_ADAPTER_PREFIX; pub type PIP_ADAPTER_PREFIX = *mut _IP_ADAPTER_PREFIX; #[repr(C)] pub struct _IP_ADAPTER_WINS_SERVER_ADDRESS_LH { pub Length: ULONG, pub Reserved: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_WINS_SERVER_ADDRESS_LH, pub Address: SOCKET_ADDRESS, } pub type IP_ADAPTER_WINS_SERVER_ADDRESS_LH = _IP_ADAPTER_WINS_SERVER_ADDRESS_LH; pub type PIP_ADAPTER_WINS_SERVER_ADDRESS_LH = *mut _IP_ADAPTER_WINS_SERVER_ADDRESS_LH; pub type IP_ADAPTER_WINS_SERVER_ADDRESS = _IP_ADAPTER_WINS_SERVER_ADDRESS_LH; pub type PIP_ADAPTER_WINS_SERVER_ADDRESS = *mut _IP_ADAPTER_WINS_SERVER_ADDRESS_LH; #[repr(C)] pub struct _IP_ADAPTER_GATEWAY_ADDRESS_LH { pub Length: ULONG, pub Reserved: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_GATEWAY_ADDRESS_LH, pub Address: SOCKET_ADDRESS, } pub type IP_ADAPTER_GATEWAY_ADDRESS_LH = _IP_ADAPTER_GATEWAY_ADDRESS_LH; pub type PIP_ADAPTER_GATEWAY_ADDRESS_LH = *mut _IP_ADAPTER_GATEWAY_ADDRESS_LH; pub type IP_ADAPTER_GATEWAY_ADDRESS = _IP_ADAPTER_GATEWAY_ADDRESS_LH; pub type PIP_ADAPTER_GATEWAY_ADDRESS = *mut _IP_ADAPTER_GATEWAY_ADDRESS_LH; pub type NET_IF_COMPARTMENT_ID = UINT32; pub type PNET_IF_COMPARTMENT_ID = *mut UINT32; pub type NET_IF_NETWORK_GUID = guiddef::GUID; pub type PNET_IF_NETWORK_GUID = *mut guiddef::GUID; #[repr(C)] pub enum _NET_IF_CONNECTION_TYPE { NET_IF_CONNECTION_DEDICATED = 1, NET_IF_CONNECTION_PASSIVE = 2, NET_IF_CONNECTION_DEMAND = 3, NET_IF_CONNECTION_MAXIMUM = 4, } pub type NET_IF_CONNECTION_TYPE = _NET_IF_CONNECTION_TYPE; pub type PNET_IF_CONNECTION_TYPE = *mut _NET_IF_CONNECTION_TYPE; #[repr(C)] pub struct _NET_LUID_LH { pub Value: ULONG64, } pub type NET_LUID_LH = _NET_LUID_LH; pub type PNER_LUID_LH = *mut _NET_LUID_LH; pub type NET_LUID = NET_LUID_LH; pub type PNET_LUID = *mut NET_LUID; pub type IF_LUID = NET_LUID; pub type PIF_LUID = *mut NET_LUID; #[repr(C)] pub enum TUNNEL_TYPE { TUNNEL_TYPE_NONE = 0, TUNNEL_TYPE_OTHER = 1, TUNNEL_TYPE_DIRECT = 2, TUNNEL_TYPE_6TO4 = 11, TUNNEL_TYPE_ISATAP = 13, TUNNEL_TYPE_TEREDO = 14, TUNNEL_TYPE_IPHTTPS = 15, } pub type PTUNNEL_TYPE = *mut TUNNEL_TYPE; #[repr(C)] pub struct _IP_ADAPTER_DNS_SUFFIX { pub Next: *mut _IP_ADAPTER_DNS_SUFFIX, pub String: [winnt::WCHAR; MAX_DNS_SUFFIX_STRING_LENGTH], } pub type IP_ADAPTER_DNS_SUFFIX = _IP_ADAPTER_DNS_SUFFIX; pub type PIP_ADAPTER_DNS_SUFFIX = *mut _IP_ADAPTER_DNS_SUFFIX; #[repr(C)] pub struct _IP_ADAPTER_ADDRESSES { pub Length: ULONG, pub IfIndex: minwindef::DWORD, pub Next: *mut _IP_ADAPTER_ADDRESSES, pub AdapterName: PCHAR, pub FirstUnicastAddress: PIP_ADAPTER_UNICAST_ADDRESS, pub FirstAnycastAddress: PIP_ADAPTER_ANYCAST_ADDRESS, pub FirstMulticastAddress: PIP_ADAPTER_MULTICAST_ADDRESS, pub FirstDnsServerAddress: PIP_ADAPTER_DNS_SERVER_ADDRESS, pub DnsSuffix: PWCHAR, pub Description: PWCHAR, pub FriendlyName: PWCHAR, pub PhysicalAddress: [minwindef::BYTE; MAX_ADAPTER_ADDRESS_LENGTH], pub PhysicalAddressLength: minwindef::DWORD, pub Flags: minwindef::DWORD, pub Mtu: minwindef::DWORD, pub IfType: minwindef::DWORD, pub OperStatus: IF_OPER_STATUS, pub Ipv6IfIndex: minwindef::DWORD, pub ZoneIndices: [minwindef::DWORD; 16], pub FirstPrefix: PIP_ADAPTER_PREFIX, pub TransmitLinkSpeed: ULONG64, pub ReceiveLinkSpeed: ULONG64, pub FirstWinsServerAddress: PIP_ADAPTER_WINS_SERVER_ADDRESS_LH, pub FirstGatewayAddress: PIP_ADAPTER_GATEWAY_ADDRESS_LH, pub Ipv4Metric: ULONG, pub Ipv6Metric: ULONG, pub Luid: IF_LUID, pub Dhcpv4Server: SOCKET_ADDRESS, pub CompartmentId: NET_IF_COMPARTMENT_ID, pub NetworkGuid: NET_IF_NETWORK_GUID, pub ConnectionType: NET_IF_CONNECTION_TYPE, pub TunnelType: TUNNEL_TYPE, pub Dhcpv6Server: SOCKET_ADDRESS, pub Dhcpv6ClientDuid: [minwindef::BYTE; MAX_DHCPV6_DUID_LENGTH], pub Dhcpv6ClientDuidLength: ULONG, pub Dhcpv6Iaid: ULONG, pub FirstDnsSuffix: PIP_ADAPTER_DNS_SUFFIX, } pub type IP_ADAPTER_ADDRESSES = _IP_ADAPTER_ADDRESSES; pub type PIP_ADAPTER_ADDRESSES = *mut _IP_ADAPTER_ADDRESSES; #[link(name = "iphlpapi")] extern "system" { // from IPHlpApi.h pub fn GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO, pOutBufLen: PULONG) -> minwindef::DWORD; pub fn GetAdaptersAddresses( Family: ULONG, Flags: ULONG, Reserved: PVOID, AdapterAddresses: PIP_ADAPTER_ADDRESSES, SizePointer: PULONG, ) -> minwindef::DWORD; } #[link(name = "Packet")] #[allow(improper_ctypes)] extern "C" { // from Packet32.h pub fn PacketSendPacket( AdapterObject: LPADAPTER, pPacket: LPPACKET, Sync: winnt::BOOLEAN, ) -> winnt::BOOLEAN; pub fn PacketReceivePacket( AdapterObject: LPADAPTER, lpPacket: LPPACKET, Sync: winnt::BOOLEAN, ) -> winnt::BOOLEAN; pub fn PacketAllocatePacket() -> LPPACKET; pub fn PacketInitPacket(lpPacket: LPPACKET, Buffer: PVOID, Length: UINT); pub fn PacketFreePacket(lpPacket: LPPACKET); pub fn PacketOpenAdapter(AdapterName: PCHAR) -> LPADAPTER; pub fn PacketCloseAdapter(lpAdapter: LPADAPTER); pub fn PacketGetAdapterNames(pStr: PTSTR, BufferSize: PULONG) -> winnt::BOOLEAN; pub fn PacketSetHwFilter(AdapterObject: LPADAPTER, Filter: ULONG) -> winnt::BOOLEAN; pub fn PacketSetMinToCopy(AdapterObject: LPADAPTER, nbytes: ctypes::c_int) -> winnt::BOOLEAN; pub fn PacketSetBuff(AdapterObject: LPADAPTER, dim: ctypes::c_int) -> winnt::BOOLEAN; pub fn PacketSetReadTimeout(AdapterObject: LPADAPTER, timeout: ctypes::c_int) -> winnt::BOOLEAN; } pnet_datalink-0.35.0/src/bpf.rs000064400000000000000000000360341046102023000144400ustar 00000000000000// Copyright (c) 2014-2016 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Support for sending and receiving data link layer packets using the /dev/bpf device. use crate::bindings::bpf; use crate::{DataLinkReceiver, DataLinkSender, NetworkInterface}; use pnet_sys; use std::collections::VecDeque; use std::ffi::CString; use std::io; use std::mem::{self, align_of}; use std::ptr; use std::sync::Arc; use std::time::Duration; static ETHERNET_HEADER_SIZE: usize = 14; static NULL_HEADER_SIZE: usize = 4; /// The BPF-specific configuration. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Config { /// The size of buffer to use when writing packets. Defaults to 4096. pub write_buffer_size: usize, /// The size of buffer to use when reading packets. Defaults to 4096. pub read_buffer_size: usize, /// The read timeout. Defaults to None. pub read_timeout: Option, /// The write timeout. Defaults to None. pub write_timeout: Option, /// The number of /dev/bpf* file descriptors to attempt before failing. /// /// This setting is only used on OS X - FreeBSD uses a single /dev/bpf rather than creating a /// new descriptor each time one is opened. /// /// Defaults to: 1000. pub bpf_fd_attempts: usize, } impl<'a> From<&'a super::Config> for Config { fn from(config: &super::Config) -> Config { Config { write_buffer_size: config.write_buffer_size, read_buffer_size: config.read_buffer_size, bpf_fd_attempts: config.bpf_fd_attempts, read_timeout: config.read_timeout, write_timeout: config.write_timeout, } } } impl Default for Config { fn default() -> Config { Config { write_buffer_size: 4096, read_buffer_size: 4096, bpf_fd_attempts: 1000, read_timeout: None, write_timeout: None, } } } /// Create a datalink channel using the /dev/bpf device // NOTE buffer must be word aligned. #[inline] pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result { #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris" ))] fn get_fd(_attempts: usize) -> libc::c_int { let c_file_name = CString::new(&b"/dev/bpf"[..]).unwrap(); unsafe { libc::open( c_file_name.as_ptr(), libc::O_RDWR, 0, ) } } #[cfg(any(target_os = "openbsd", target_os = "macos", target_os = "ios", target_os = "tvos"))] fn get_fd(attempts: usize) -> libc::c_int { for i in 0..attempts { let fd = unsafe { let file_name = format!("/dev/bpf{}", i); let c_file_name = CString::new(file_name.as_bytes()).unwrap(); libc::open( c_file_name.as_ptr(), libc::O_RDWR, 0, ) }; if fd != -1 { return fd; } } -1 } #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris" ))] fn set_feedback(fd: libc::c_int) -> io::Result<()> { if unsafe { bpf::ioctl(fd, bpf::BIOCFEEDBACK, &1) } == -1 { let err = io::Error::last_os_error(); unsafe { libc::close(fd); } return Err(err); } Ok(()) } #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "ios", target_os = "tvos"))] fn set_feedback(_fd: libc::c_int) -> io::Result<()> { Ok(()) } let fd = get_fd(config.bpf_fd_attempts); if fd == -1 { return Err(io::Error::last_os_error()); } let mut iface: bpf::ifreq = unsafe { mem::zeroed() }; for (i, c) in network_interface.name.bytes().enumerate() { iface.ifr_name[i] = c as libc::c_char; } let buflen = config.read_buffer_size as libc::c_uint; // NOTE Buffer length must be set before binding to an interface // otherwise this will return Invalid Argument if unsafe { bpf::ioctl(fd, bpf::BIOCSBLEN, &buflen) } == -1 { let err = io::Error::last_os_error(); unsafe { libc::close(fd); } return Err(err); } // Set the interface to use if unsafe { bpf::ioctl(fd, bpf::BIOCSETIF, &iface) } == -1 { let err = io::Error::last_os_error(); unsafe { libc::close(fd); } return Err(err); } // Return from read as soon as packets are available - don't wait to fill the // buffer if unsafe { bpf::ioctl(fd, bpf::BIOCIMMEDIATE, &1) } == -1 { let err = io::Error::last_os_error(); unsafe { libc::close(fd); } return Err(err); } // Get the device type let mut dlt: libc::c_uint = 0; if unsafe { bpf::ioctl(fd, bpf::BIOCGDLT, &mut dlt) } == -1 { let err = io::Error::last_os_error(); unsafe { libc::close(fd); } return Err(err); } let mut loopback = false; let mut buffer_offset = 0; let mut allocated_read_buffer_size = config.read_buffer_size; // The loopback device does weird things // FIXME This should really just be another L2 packet header type if dlt == bpf::DLT_NULL { loopback = true; // The loopback device provides a smaller (4-byte) header than ethernet (14-byte). // We deal with this by offsetting the write buffer, then overwriting the null header // with a zeroed ethernet header. This is complicated by the fact that the buffer // offset must be a multiple of four for pointer alignment, and that the write itself // must be 4096 bytes. let align = align_of::(); buffer_offset = (ETHERNET_HEADER_SIZE - NULL_HEADER_SIZE).next_multiple_of(align); allocated_read_buffer_size += buffer_offset; // Allow packets to be read back after they are written if let Err(e) = set_feedback(fd) { return Err(e); } } else { // Don't fill in source MAC if unsafe { bpf::ioctl(fd, bpf::BIOCSHDRCMPLT, &1) } == -1 { let err = io::Error::last_os_error(); unsafe { libc::close(fd); } return Err(err); } } // Enable nonblocking if unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) } == -1 { let err = io::Error::last_os_error(); unsafe { pnet_sys::close(fd); } return Err(err); } let fd = Arc::new(pnet_sys::FileDesc { fd: fd }); let mut sender = Box::new(DataLinkSenderImpl { fd: fd.clone(), fd_set: unsafe { mem::zeroed() }, write_buffer: vec![0; config.write_buffer_size], loopback: loopback, timeout: config .write_timeout .map(|to| pnet_sys::duration_to_timespec(to)), }); unsafe { libc::FD_ZERO(&mut sender.fd_set as *mut libc::fd_set); libc::FD_SET(fd.fd, &mut sender.fd_set as *mut libc::fd_set); } let mut receiver = Box::new(DataLinkReceiverImpl { fd: fd.clone(), fd_set: unsafe { mem::zeroed() }, read_buffer: vec![0; allocated_read_buffer_size], buffer_offset, loopback: loopback, timeout: config .read_timeout .map(|to| pnet_sys::duration_to_timespec(to)), // Enough room for minimally sized packets without reallocating packets: VecDeque::with_capacity(allocated_read_buffer_size / 64), }); unsafe { libc::FD_ZERO(&mut receiver.fd_set as *mut libc::fd_set); libc::FD_SET(fd.fd, &mut receiver.fd_set as *mut libc::fd_set); } Ok(super::Channel::Ethernet(sender, receiver)) } struct DataLinkSenderImpl { fd: Arc, fd_set: libc::fd_set, write_buffer: Vec, loopback: bool, timeout: Option, } impl DataLinkSender for DataLinkSenderImpl { #[inline] fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut dyn FnMut(&mut [u8]), ) -> Option> { let len = num_packets * packet_size; if len >= self.write_buffer.len() { None } else { // If we're sending on the loopback device, discard the ethernet header. // The OS will prepend the packet with 4 bytes set to AF_INET. let offset = if self.loopback { ETHERNET_HEADER_SIZE } else { 0 }; for chunk in self.write_buffer[..len].chunks_mut(packet_size) { func(chunk); let ret = unsafe { libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set); libc::pselect( self.fd.fd + 1, ptr::null_mut(), &mut self.fd_set as *mut libc::fd_set, ptr::null_mut(), self.timeout .as_ref() .map(|to| to as *const libc::timespec) .unwrap_or(ptr::null()), ptr::null(), ) }; if ret == -1 { // Error occurred! return Some(Err(io::Error::last_os_error())); } else if ret == 0 { return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"))); } else { match unsafe { libc::write( self.fd.fd, chunk.as_ptr().offset(offset as isize) as *const libc::c_void, (chunk.len() - offset) as libc::size_t, ) } { len if len == -1 => return Some(Err(io::Error::last_os_error())), _ => (), } } } Some(Ok(())) } } #[inline] fn send_to(&mut self, packet: &[u8], _dst: Option) -> Option> { // If we're sending on the loopback device, discard the ethernet header. // The OS will prepend the packet with 4 bytes set to AF_INET. let offset = if self.loopback { ETHERNET_HEADER_SIZE } else { 0 }; let ret = unsafe { libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set); libc::pselect( self.fd.fd + 1, ptr::null_mut(), &mut self.fd_set as *mut libc::fd_set, ptr::null_mut(), self.timeout .as_ref() .map(|to| to as *const libc::timespec) .unwrap_or(ptr::null()), ptr::null(), ) }; if ret == -1 { // Error occurred! return Some(Err(io::Error::last_os_error())); } else if ret == 0 { return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"))); } else { match unsafe { libc::write( self.fd.fd, packet.as_ptr().offset(offset as isize) as *const libc::c_void, (packet.len() - offset) as libc::size_t, ) } { len if len == -1 => Some(Err(io::Error::last_os_error())), _ => Some(Ok(())), } } } } struct DataLinkReceiverImpl { fd: Arc, fd_set: libc::fd_set, read_buffer: Vec, buffer_offset: usize, loopback: bool, timeout: Option, packets: VecDeque<(usize, usize)>, } impl DataLinkReceiver for DataLinkReceiverImpl { fn next(&mut self) -> io::Result<&[u8]> { let header_size = if self.loopback { NULL_HEADER_SIZE } else { 0 }; if self.packets.is_empty() { let buffer = &mut self.read_buffer[self.buffer_offset..]; let ret = unsafe { libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set); libc::pselect( self.fd.fd + 1, &mut self.fd_set as *mut libc::fd_set, ptr::null_mut(), ptr::null_mut(), self.timeout .as_ref() .map(|to| to as *const libc::timespec) .unwrap_or(ptr::null()), ptr::null(), ) }; if ret == -1 { return Err(io::Error::last_os_error()); } else if ret == 0 { return Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")); } else { let buflen = match unsafe { libc::read( self.fd.fd, buffer.as_ptr() as *mut libc::c_void, buffer.len() as libc::size_t, ) } { len if len > 0 => len, _ => return Err(io::Error::last_os_error()), }; let mut ptr = buffer.as_mut_ptr(); let end = unsafe { buffer.as_ptr().offset(buflen as isize) }; while (ptr as *const u8) < end { unsafe { let packet: *const bpf::bpf_hdr = mem::transmute(ptr); let start = ptr as isize + (*packet).bh_hdrlen as isize - buffer.as_ptr() as isize; self.packets.push_back(( start as usize + header_size, (*packet).bh_caplen as usize - header_size, )); let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize; ptr = ptr.offset(bpf::BPF_WORDALIGN(offset)); } } } } let (start, mut len) = self.packets.pop_front().unwrap(); len += self.buffer_offset; // Zero out part that will become fake ethernet header if on loopback. for i in (&mut self.read_buffer[start..start + self.buffer_offset]).iter_mut() { *i = 0; } Ok(&self.read_buffer[start..start + len]) } } /// Get a list of available network interfaces for the current machine. pub fn interfaces() -> Vec { #[path = "unix_interfaces.rs"] mod interfaces; interfaces::interfaces() } pnet_datalink-0.35.0/src/dummy.rs000064400000000000000000000251631046102023000150250ustar 00000000000000// Copyright (c) 2016 Linus Färnstrand // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Support for sending and receiving data link layer packets on a fake network managed //! by in memory FIFO queues. Useful for writing tests. use crate::{DataLinkReceiver, DataLinkSender, MacAddr, NetworkInterface}; use std::io; use std::sync::mpsc::{self, Receiver, Sender}; use std::thread; use std::time; /// Configuration for the dummy datalink backend. Contains `std::sync::mpsc` /// channels that are used to communicate with the fake network. #[derive(Debug)] pub struct Config { receiver: Receiver>>, inject_handle: Option>>>, sender: Sender>, read_handle: Option>>, } impl Config { /// Creates a new `Config` with the given channels as the backing network. /// When using this constructor `inject_handle` and `read_handle` will return `None`. /// Those handles must be kept track of elsewhere. /// /// The `DataLinkReceiver` created by the dummy backend will read packets from /// `receiver`. Both network errors and data can be sent on this channel. /// When the `receiver` channel is closed (`Sender` is dropped) /// `DataLinkReceiver::next()` will sleep forever, simlating an idle network. /// /// The `DataLinkSender` created by the dummy backend will send all packets sent /// through `build_and_send()` and `send_to()` to the `sender` channel. pub fn new(receiver: Receiver>>, sender: Sender>) -> Config { Config { receiver: receiver, inject_handle: None, sender: sender, read_handle: None, } } /// Get the `Sender` handle that can inject packets in the fake network. /// Only usable with `Config`s generated from `default()`. pub fn inject_handle(&mut self) -> Option>>> { self.inject_handle.take() } /// Get the `Receiver` handle where packets sent to the fake network can be read. /// Only usable with `Config`s generated from `default()`. pub fn read_handle(&mut self) -> Option>> { self.read_handle.take() } } impl<'a> From<&'a super::Config> for Config { /// Will not use the `super::Config`. This will simply call `dummy::Config::default()`. fn from(_config: &super::Config) -> Config { Config::default() } } impl Default for Config { /// Creates a default config with one input and one output channel. The handles used to inject /// to and read form the network can be fetched with `inject_handle()` and `read_handle()`. fn default() -> Config { let (in_tx, in_rx) = mpsc::channel(); let (out_tx, out_rx) = mpsc::channel(); Config { receiver: in_rx, inject_handle: Some(in_tx), sender: out_tx, read_handle: Some(out_rx), } } } /// Create a data link channel backed by FIFO queues. Useful for debugging and testing. /// See `Config` for how to inject and read packets on this fake network. pub fn channel(_: &NetworkInterface, config: Config) -> io::Result { let sender = Box::new(MockDataLinkSender { sender: config.sender, }); let receiver = Box::new(MockDataLinkReceiver { receiver: config.receiver, used_packets: Vec::new(), }); Ok(super::Channel::Ethernet(sender, receiver)) } struct MockDataLinkSender { sender: Sender>, } impl DataLinkSender for MockDataLinkSender { fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut dyn FnMut(&mut [u8]), ) -> Option> { for _ in 0..num_packets { let mut buffer = vec![0; packet_size]; func(&mut buffer); // Send the data to the queue. Don't care if it's closed self.sender.send(buffer.into_boxed_slice()).unwrap_or(()); } Some(Ok(())) } fn send_to(&mut self, packet: &[u8], _dst: Option) -> Option> { let buffer = packet.to_vec(); self.sender.send(buffer.into_boxed_slice()).unwrap_or(()); Some(Ok(())) } } struct MockDataLinkReceiver { receiver: Receiver>>, used_packets: Vec>, } impl DataLinkReceiver for MockDataLinkReceiver { fn next(&mut self) -> io::Result<&[u8]> { match self.receiver.recv() { Ok(result) => { // A network event happened. Might be a packet or a simulated error match result { Ok(buffer) => { self.used_packets.push(buffer); let buffer_ref = &*self.used_packets[self.used_packets.len() - 1]; Ok(buffer_ref) } Err(e) => Err(e), } } Err(_) => { // The channel supplying fake packets is broken. The user lost/destroyed their // inject_handle. This means there will never be any more packets sent to this // dummy network. To simulate an idle network we block and sleep forever here. loop { thread::sleep(time::Duration::new(10, 0)); } } } } } /// Get three fake interfaces generated with `dummy_interface(0..3)`. pub fn interfaces() -> Vec { (0..3).map(|i| dummy_interface(i)).collect() } /// Generates a fake `NetworkInterface`. /// The name of the interface will be `ethX` where X is the integer `i`. /// The index will be `i`. /// The MAC will be `01:02:03:04:05:i`. pub fn dummy_interface(i: u8) -> NetworkInterface { NetworkInterface { name: format!("eth{}", i), description: "".to_string(), index: i as u32, mac: Some(MacAddr::new(1, 2, 3, 4, 5, i)), ips: Vec::new(), flags: 0, } } #[cfg(test)] mod tests { use crate::{DataLinkReceiver, DataLinkSender}; use std::io; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; use std::thread::{sleep, spawn}; use std::time::Duration; #[test] fn send_nothing() { let (_, read_handle, mut tx, _) = create_net(); // Check that sending zero packets yields zero packets let mut builder = |_: &mut [u8]| { panic!("Should not be called"); }; tx.build_and_send(0, 20, &mut builder).unwrap().unwrap(); assert!(read_handle.try_recv().is_err()); } #[test] fn send_one_packet() { let (_, read_handle, mut tx, _) = create_net(); // Check that sending one packet yields one packet let mut builder = |pkg: &mut [u8]| { assert_eq!(pkg.len(), 20); pkg[0] = 9; pkg[19] = 201; }; tx.build_and_send(1, 20, &mut builder).unwrap().unwrap(); let pkg = read_handle .try_recv() .expect("Expected one packet to be sent"); assert!(read_handle.try_recv().is_err()); assert_eq!(pkg.len(), 20); assert_eq!(pkg[0], 9); assert_eq!(pkg[19], 201); } #[test] fn send_multiple_packets() { let (_, read_handle, mut tx, _) = create_net(); // Check that sending multiple packets does the correct thing let mut closure_counter = 0; let mut builder = |pkg: &mut [u8]| { pkg[0] = closure_counter; closure_counter += 1; }; tx.build_and_send(3, 20, &mut builder).unwrap().unwrap(); for i in 0..3 { let pkg = read_handle.try_recv().expect("Expected a packet"); assert_eq!(pkg[0], i); } assert!(read_handle.try_recv().is_err()); } #[test] fn send_to() { let (_, read_handle, mut tx, _) = create_net(); let mut buffer = vec![0; 20]; buffer[1] = 34; buffer[18] = 76; tx.send_to(&buffer, None).unwrap().unwrap(); let pkg = read_handle .try_recv() .expect("Expected one packet to be sent"); assert!(read_handle.try_recv().is_err()); assert_eq!(pkg.len(), 20); assert_eq!(pkg[1], 34); assert_eq!(pkg[18], 76); } #[test] fn read_nothing() { let (_, _, _, mut rx) = create_net(); let (control_tx, control_rx) = mpsc::channel(); spawn(move || { rx.next().expect("Should not happen 1"); control_tx.send(()).expect("Should not happen 2"); }); sleep(Duration::new(0, 1_000_000)); match control_rx.try_recv() { Ok(_) => panic!("Nothing should have arrived"), Err(TryRecvError::Disconnected) => panic!("Thread should not have quit"), Err(TryRecvError::Empty) => (), } } #[test] fn read_one_pkg() { let (inject_handle, _, _, mut rx) = create_net(); let buffer = vec![0; 20]; inject_handle.send(Ok(buffer.into_boxed_slice())).unwrap(); let pkg = rx.next().expect("Expected a packet"); assert_eq!(pkg.len(), 20); } #[test] fn read_multiple_pkgs() { let (inject_handle, _, _, mut rx) = create_net(); for i in 0..3 { let buffer = vec![i; 20]; inject_handle.send(Ok(buffer.into_boxed_slice())).unwrap(); } { let pkg1 = rx.next().expect("Expected a packet"); assert_eq!(pkg1[0], 0); } { let pkg2 = rx.next().expect("Expected a packet"); assert_eq!(pkg2[0], 1); } { let pkg3 = rx.next().expect("Expected a packet"); assert_eq!(pkg3[0], 2); } } fn create_net() -> ( Sender>>, Receiver>, Box, Box, ) { let interface = super::dummy_interface(56); let mut config = super::Config::default(); let inject_handle = config.inject_handle().unwrap(); let read_handle = config.read_handle().unwrap(); let channel = super::channel(&interface, config); let (tx, rx) = match channel { Ok(super::super::Channel::Ethernet(tx, rx)) => (tx, rx), _ => panic!("Not a valid channel returned"), }; (inject_handle, read_handle, tx, rx) } } pnet_datalink-0.35.0/src/lib.rs000064400000000000000000000304561046102023000144410ustar 00000000000000// Copyright (c) 2014-2016 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Support for sending and receiving data link layer packets. #![deny(warnings)] extern crate ipnetwork; extern crate libc; extern crate pnet_base; extern crate pnet_sys; #[cfg(feature = "serde")] extern crate serde; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::io; use std::option::Option; use std::time::Duration; use ipnetwork::IpNetwork; pub use pnet_base::{MacAddr, ParseMacAddrErr}; mod bindings; #[cfg(windows)] #[path = "winpcap.rs"] mod backend; #[cfg(windows)] pub mod winpcap; #[cfg(all( not(feature = "netmap"), any(target_os = "linux", target_os = "android") ))] #[path = "linux.rs"] mod backend; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod linux; #[cfg(all( not(feature = "netmap"), any( target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "macos", target_os = "ios", target_os = "tvos" ) ))] #[path = "bpf.rs"] mod backend; #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "macos", target_os = "ios", target_os = "tvos" ))] pub mod bpf; #[cfg(feature = "netmap")] #[path = "netmap.rs"] mod backend; #[cfg(feature = "netmap")] pub mod netmap; #[cfg(feature = "pcap")] pub mod pcap; pub mod dummy; /// Type alias for an `EtherType`. pub type EtherType = u16; /// Type of data link channel to present (Linux only). #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum ChannelType { /// Send and receive layer 2 packets directly, including headers. Layer2, /// Send and receive "cooked" packets - send and receive network layer packets. Layer3(EtherType), } /// A channel for sending and receiving at the data link layer. #[non_exhaustive] pub enum Channel { /// A datalink channel which sends and receives Ethernet packets. Ethernet(Box, Box), } /// Socket fanout type (Linux only). #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum FanoutType { HASH, LB, CPU, ROLLOVER, RND, QM, CBPF, EBPF, } /// Fanout settings (Linux only). #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct FanoutOption { pub group_id: u16, pub fanout_type: FanoutType, pub defrag: bool, pub rollover: bool, } /// A generic configuration type, encapsulating all options supported by each backend. /// /// Each option should be treated as a hint - each backend is free to ignore any and all /// options which don't apply to it. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Config { /// The size of buffer to use when writing packets. Defaults to 4096. pub write_buffer_size: usize, /// The size of buffer to use when reading packets. Defaults to 4096. pub read_buffer_size: usize, /// Linux/BPF/Netmap only: The read timeout. Defaults to None. pub read_timeout: Option, /// Linux/BPF/Netmap only: The write timeout. Defaults to None. pub write_timeout: Option, /// Linux only: Specifies whether to read packets at the datalink layer or network layer. /// Defaults to Layer2 pub channel_type: ChannelType, /// BPF/OS X only: The number of /dev/bpf* file descriptors to attempt before failing. Defaults /// to: 1000. pub bpf_fd_attempts: usize, pub linux_fanout: Option, pub promiscuous: bool, /// Linux only: The socket's file descriptor that pnet will use pub socket_fd: Option, } impl Default for Config { fn default() -> Config { Config { write_buffer_size: 4096, read_buffer_size: 4096, read_timeout: None, write_timeout: None, channel_type: ChannelType::Layer2, bpf_fd_attempts: 1000, linux_fanout: None, promiscuous: true, socket_fd: None, } } } /// Create a new datalink channel for sending and receiving data. /// /// This allows for sending and receiving packets at the data link layer. /// /// A list of network interfaces can be retrieved using datalink::interfaces(). /// /// The configuration serves as a hint to the backend - some or all of it may be used or ignored, /// depending on which backend is used. /// /// When matching on the returned channel, make sure to include a catch-all so that code doesn't /// break when new channel types are added. #[inline] pub fn channel( network_interface: &NetworkInterface, configuration: Config, ) -> io::Result { backend::channel( network_interface, (&configuration).into() ) } /// Trait to enable sending `$packet` packets. pub trait DataLinkSender: Send { /// Create and send a number of packets. /// /// This will call `func` `num_packets` times. The function will be provided with a /// mutable packet to manipulate, which will then be sent. This allows packets to be /// built in-place, avoiding the copy required for `send`. If there is not sufficient /// capacity in the buffer, None will be returned. fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut dyn FnMut(&mut [u8]), ) -> Option>; /// Send a packet. /// /// This may require an additional copy compared to `build_and_send`, depending on the /// operating system being used. The second parameter is currently ignored, however /// `None` should be passed. fn send_to(&mut self, packet: &[u8], dst: Option) -> Option>; } /// Structure for receiving packets at the data link layer. Should be constructed using /// `datalink_channel()`. pub trait DataLinkReceiver: Send { /// Get the next ethernet frame in the channel. fn next(&mut self) -> io::Result<&[u8]>; } /// Represents a network interface and its associated addresses. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct NetworkInterface { /// The name of the interface. pub name: String, /// A description of the interface. pub description: String, /// The interface index (operating system specific). pub index: u32, /// A MAC address for the interface. pub mac: Option, /// IP addresses and netmasks for the interface. pub ips: Vec, /// Operating system specific flags for the interface. #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] pub flags: u32, #[cfg(any(target_os = "illumos", target_os = "solaris"))] pub flags: u64, } /// Type alias for an `InterfaceType`. #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] pub type InterfaceType = u32; #[cfg(any(target_os = "illumos", target_os = "solaris"))] pub type InterfaceType = u64; impl NetworkInterface { pub fn is_up(&self) -> bool { self.flags & (pnet_sys::IFF_UP as InterfaceType) != 0 } pub fn is_broadcast(&self) -> bool { self.flags & (pnet_sys::IFF_BROADCAST as InterfaceType) != 0 } /// Is the interface a loopback interface? pub fn is_loopback(&self) -> bool { self.flags & (pnet_sys::IFF_LOOPBACK as InterfaceType) != 0 } pub fn is_point_to_point(&self) -> bool { self.flags & (pnet_sys::IFF_POINTOPOINT as InterfaceType) != 0 } pub fn is_multicast(&self) -> bool { self.flags & (pnet_sys::IFF_MULTICAST as InterfaceType) != 0 } /// Triggered when the driver has signated netif_carrier_on /// Check for more information #[cfg(any(target_os = "linux", target_os = "android"))] pub fn is_lower_up(&self) -> bool { self.flags & (pnet_sys::IFF_LOWER_UP as InterfaceType) != 0 } /// Triggered when the driver has signated netif_dormant_on /// Check for more information #[cfg(any(target_os = "linux", target_os = "android"))] pub fn is_dormant(&self) -> bool { self.flags & (pnet_sys::IFF_DORMANT as InterfaceType) != 0 } #[cfg(unix)] pub fn is_running(&self) -> bool { self.flags & (pnet_sys::IFF_RUNNING as InterfaceType) != 0 } } impl ::std::fmt::Display for NetworkInterface { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { const FLAGS: [&'static str; 8] = [ "UP", "BROADCAST", "LOOPBACK", "POINTOPOINT", "MULTICAST", "RUNNING", "DORMANT", "LOWERUP", ]; let flags = if self.flags > 0 { #[cfg(any(target_os = "linux", target_os = "android"))] let rets = [ self.is_up(), self.is_broadcast(), self.is_loopback(), self.is_point_to_point(), self.is_multicast(), self.is_running(), self.is_dormant(), self.is_lower_up(), ]; #[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))] let rets = [ self.is_up(), self.is_broadcast(), self.is_loopback(), self.is_point_to_point(), self.is_multicast(), self.is_running(), false, false, ]; #[cfg(not(unix))] let rets = [ self.is_up(), self.is_broadcast(), self.is_loopback(), self.is_point_to_point(), self.is_multicast(), false, false, false, ]; format!( "{:X}<{}>", self.flags, rets.iter() .zip(FLAGS.iter()) .filter(|&(ret, _)| ret == &true) .map(|(_, name)| name.to_string()) .collect::>() .join(",") ) } else { format!("{:X}", self.flags) }; let mac = self .mac .map(|mac| mac.to_string()) .unwrap_or("N/A".to_owned()); let ips = if self.ips.len() > 0 { format!( "\n{}", self.ips .iter() .map(|ip| { if ip.is_ipv4() { format!(" inet: {}", ip) } else { format!(" inet6: {}", ip) } }) .collect::>() .join("\n") ) } else { "".to_string() }; write!( f, "{}: flags={} index: {} ether: {}{}", self.name, flags, self.index, mac, ips ) } } /// Get a list of available network interfaces for the current machine. /// /// If you need the default network interface, you can choose the first /// one that is up, not loopback and has an IP. This is not guaranteed to /// work on each system but should work for basic packet sniffing: /// /// ``` /// use pnet_datalink::interfaces; /// /// // Get a vector with all network interfaces found /// let all_interfaces = interfaces(); /// /// // Search for the default interface - the one that is /// // up, not loopback and has an IP. /// let default_interface = all_interfaces /// .iter() /// .find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty()); /// /// match default_interface { /// Some(interface) => println!("Found default interface with [{}].", interface.name), /// None => println!("Error while finding the default interface."), /// } /// /// ``` /// pub fn interfaces() -> Vec { backend::interfaces() } pnet_datalink-0.35.0/src/linux.rs000064400000000000000000000327431046102023000150330ustar 00000000000000// Copyright (c) 2014-2016 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Support for sending and receiving data link layer packets using Linux's `AF_PACKET`. extern crate libc; use crate::bindings::linux; use crate::{DataLinkReceiver, DataLinkSender, MacAddr, NetworkInterface}; use pnet_sys; use std::io; use std::mem; use std::sync::Arc; use std::time::Duration; fn network_addr_to_sockaddr( ni: &NetworkInterface, storage: *mut libc::sockaddr_storage, proto: libc::c_int, ) -> usize { unsafe { let sll: *mut libc::sockaddr_ll = mem::transmute(storage); (*sll).sll_family = libc::AF_PACKET as libc::sa_family_t; if let Some(MacAddr(a, b, c, d, e, f)) = ni.mac { (*sll).sll_addr = [a, b, c, d, e, f, 0, 0]; } (*sll).sll_protocol = (proto as u16).to_be(); (*sll).sll_halen = 6; (*sll).sll_ifindex = ni.index as i32; mem::size_of::() } } /// Configuration for the Linux datalink backend. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Config { /// The size of buffer to use when writing packets. Defaults to 4096. pub write_buffer_size: usize, /// The size of buffer to use when reading packets. Defaults to 4096. pub read_buffer_size: usize, /// The read timeout. Defaults to None. pub read_timeout: Option, /// The write timeout. Defaults to None. pub write_timeout: Option, /// Specifies whether to read packets at the datalink layer or network layer. /// NOTE FIXME Currently ignored. /// Defaults to Layer2. pub channel_type: super::ChannelType, /// Specifies packet fanout option, if desired. Defaults to None. pub fanout: Option, /// Promiscuous mode. pub promiscuous: bool, pub socket_fd: Option, } impl<'a> From<&'a super::Config> for Config { fn from(config: &super::Config) -> Config { Config { write_buffer_size: config.write_buffer_size, read_buffer_size: config.read_buffer_size, channel_type: config.channel_type, read_timeout: config.read_timeout, write_timeout: config.write_timeout, fanout: config.linux_fanout, promiscuous: config.promiscuous, socket_fd: config.socket_fd, } } } impl Default for Config { fn default() -> Config { Config { write_buffer_size: 4096, read_buffer_size: 4096, read_timeout: None, write_timeout: None, channel_type: super::ChannelType::Layer2, fanout: None, promiscuous: true, socket_fd: None, } } } /// Create a data link channel using the Linux's `AF_PACKET` socket type. #[inline] pub fn channel( network_interface: &NetworkInterface, config: Config, ) -> io::Result { let (_typ, proto) = match config.channel_type { super::ChannelType::Layer2 => (libc::SOCK_RAW, libc::ETH_P_ALL), super::ChannelType::Layer3(proto) => (libc::SOCK_DGRAM, proto as i32), }; let socket = match config.socket_fd { Some(sock) => sock, None => match unsafe { libc::socket(libc::AF_PACKET, _typ, proto.to_be()) } { -1 => return Err(io::Error::last_os_error()), fd => fd } }; let mut addr: libc::sockaddr_storage = unsafe { mem::zeroed() }; let len = network_addr_to_sockaddr(network_interface, &mut addr, proto); let send_addr = (&addr as *const libc::sockaddr_storage) as *const libc::sockaddr; // Bind to interface if unsafe { libc::bind(socket, send_addr, len as libc::socklen_t) } == -1 { let err = io::Error::last_os_error(); unsafe { pnet_sys::close(socket); } return Err(err); } let mut pmr: linux::packet_mreq = unsafe { mem::zeroed() }; pmr.mr_ifindex = network_interface.index as i32; pmr.mr_type = linux::PACKET_MR_PROMISC as u16; // Enable promiscuous capture if config.promiscuous { if unsafe { libc::setsockopt( socket, linux::SOL_PACKET, linux::PACKET_ADD_MEMBERSHIP, (&pmr as *const linux::packet_mreq) as *const libc::c_void, mem::size_of::() as libc::socklen_t, ) } == -1 { let err = io::Error::last_os_error(); unsafe { pnet_sys::close(socket); } return Err(err); } } // Enable packet fanout if let Some(fanout) = config.fanout { use super::FanoutType; let mut typ = match fanout.fanout_type { FanoutType::HASH => linux::PACKET_FANOUT_HASH, FanoutType::LB => linux::PACKET_FANOUT_LB, FanoutType::CPU => linux::PACKET_FANOUT_CPU, FanoutType::ROLLOVER => linux::PACKET_FANOUT_ROLLOVER, FanoutType::RND => linux::PACKET_FANOUT_RND, FanoutType::QM => linux::PACKET_FANOUT_QM, FanoutType::CBPF => linux::PACKET_FANOUT_CBPF, FanoutType::EBPF => linux::PACKET_FANOUT_EBPF, } as u32; // set defrag flag if fanout.defrag { typ = typ | linux::PACKET_FANOUT_FLAG_DEFRAG; } // set rollover flag if fanout.rollover { typ = typ | linux::PACKET_FANOUT_FLAG_ROLLOVER; } // set uniqueid flag -- probably not needed atm.. // PACKET_FANOUT_FLAG_UNIQUEID -- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4a69a864209e9ab436d4a58e8028ac96cc873d15 let arg: libc::c_uint = fanout.group_id as u32 | (typ << 16); if unsafe { libc::setsockopt( socket, linux::SOL_PACKET, linux::PACKET_FANOUT, (&arg as *const libc::c_uint) as *const libc::c_void, mem::size_of::() as libc::socklen_t, ) } == -1 { let err = io::Error::last_os_error(); unsafe { pnet_sys::close(socket); } return Err(err); } } // Enable nonblocking if unsafe { libc::fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK) } == -1 { let err = io::Error::last_os_error(); unsafe { pnet_sys::close(socket); } return Err(err); } let fd = Arc::new(pnet_sys::FileDesc { fd: socket }); let sender = Box::new(DataLinkSenderImpl { socket: fd.clone(), write_buffer: vec![0; config.write_buffer_size], _channel_type: config.channel_type, send_addr: unsafe { *(send_addr as *const libc::sockaddr_ll) }, send_addr_len: len, timeout: config .write_timeout .map(|to| pnet_sys::duration_to_timespec(to)), }); let receiver = Box::new(DataLinkReceiverImpl { socket: fd.clone(), read_buffer: vec![0; config.read_buffer_size], _channel_type: config.channel_type, timeout: config .read_timeout .map(|to| pnet_sys::duration_to_timespec(to)), }); Ok(super::Channel::Ethernet(sender, receiver)) } struct DataLinkSenderImpl { socket: Arc, write_buffer: Vec, _channel_type: super::ChannelType, send_addr: libc::sockaddr_ll, send_addr_len: usize, timeout: Option, } impl DataLinkSender for DataLinkSenderImpl { // FIXME Layer 3 #[inline] fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut dyn FnMut(&mut [u8]), ) -> Option> { let len = num_packets * packet_size; if len <= self.write_buffer.len() { let min = std::cmp::min(self.write_buffer.len(), len); let mut_slice = &mut self.write_buffer; let mut pollfd = libc::pollfd { fd: self.socket.fd, events: libc::POLLOUT, // Monitoring for write ability revents: 0, // Will be filled by poll to indicate the events that occurred }; // Convert timeout to milliseconds as required by poll let timeout_ms = self .timeout .as_ref() .map(|to| (to.tv_sec as i64 * 1000) + (to.tv_nsec as i64 / 1_000_000)) .unwrap_or(-1); // -1 means wait indefinitely for chunk in mut_slice[..min].chunks_mut(packet_size) { func(chunk); let send_addr = (&self.send_addr as *const libc::sockaddr_ll) as *const libc::sockaddr; let ret = unsafe { libc::poll( &mut pollfd as *mut libc::pollfd, 1, timeout_ms as libc::c_int, ) }; if ret == -1 { return Some(Err(io::Error::last_os_error())); } else if ret == 0 { return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"))); } else if pollfd.revents & libc::POLLOUT != 0 { if let Err(e) = pnet_sys::send_to( self.socket.fd, chunk, send_addr, self.send_addr_len as libc::socklen_t, ) { return Some(Err(e)); } } else { return Some(Err(io::Error::new( io::ErrorKind::Other, "Unexpected poll event", ))); } } Some(Ok(())) } else { None } } #[inline] fn send_to(&mut self, packet: &[u8], _dst: Option) -> Option> { let mut pollfd = libc::pollfd { fd: self.socket.fd, events: libc::POLLOUT, // Monitoring for write ability revents: 0, // Will be filled by poll to indicate the events that occurred }; // Convert timeout to milliseconds as required by poll let timeout_ms = self .timeout .as_ref() .map(|to| (to.tv_sec as i64 * 1000) + (to.tv_nsec as i64 / 1_000_000)) .unwrap_or(-1); // -1 means wait indefinitely let ret = unsafe { libc::poll( &mut pollfd as *mut libc::pollfd, 1, timeout_ms as libc::c_int, ) }; if ret == -1 { Some(Err(io::Error::last_os_error())) } else if ret == 0 { Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"))) } else if pollfd.revents & libc::POLLOUT != 0 { // POLLOUT is set, meaning the socket is ready for writing match pnet_sys::send_to( self.socket.fd, packet, (&self.send_addr as *const libc::sockaddr_ll) as *const _, self.send_addr_len as libc::socklen_t, ) { Err(e) => Some(Err(e)), Ok(_) => Some(Ok(())), } } else { Some(Err(io::Error::new( io::ErrorKind::Other, "Unexpected poll event", ))) } } } struct DataLinkReceiverImpl { socket: Arc, read_buffer: Vec, _channel_type: super::ChannelType, timeout: Option, } impl DataLinkReceiver for DataLinkReceiverImpl { fn next(&mut self) -> io::Result<&[u8]> { let mut caddr: libc::sockaddr_storage = unsafe { mem::zeroed() }; let mut pollfd = libc::pollfd { fd: self.socket.fd, events: libc::POLLIN, // Monitoring for read availability revents: 0, }; // Convert timeout to milliseconds as required by poll let timeout_ms = self .timeout .as_ref() .map(|to| (to.tv_sec as i64 * 1000) + (to.tv_nsec as i64 / 1_000_000)) .unwrap_or(-1); // -1 means wait indefinitely let ret = unsafe { libc::poll( &mut pollfd as *mut libc::pollfd, 1, timeout_ms as libc::c_int, ) }; if ret == -1 { Err(io::Error::last_os_error()) } else if ret == 0 { Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")) } else if pollfd.revents & libc::POLLIN != 0 { // POLLIN is set, meaning the socket has data to be read let res = pnet_sys::recv_from(self.socket.fd, &mut self.read_buffer, &mut caddr); match res { Ok(len) => Ok(&self.read_buffer[0..len]), Err(e) => Err(e), } } else { Err(io::Error::new( io::ErrorKind::Other, "Unexpected poll event", )) } } } /// Get a list of available network interfaces for the current machine. pub fn interfaces() -> Vec { #[path = "unix_interfaces.rs"] mod interfaces; interfaces::interfaces() } pnet_datalink-0.35.0/src/netmap.rs000064400000000000000000000166521046102023000151610ustar 00000000000000// Copyright (c) 2015-2016 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Support for sending and receiving data link layer packets using the netmap library. #![allow(bad_style)] use self::netmap_sys::netmap::{netmap_slot, nm_ring_empty}; use self::netmap_sys::netmap_user::{ nm_close, nm_desc, nm_nextpkt, nm_open, nm_pkthdr, nm_ring_next, NETMAP_BUF, NETMAP_FD, NETMAP_TXRING, }; use Channel::Ethernet; use crate::{DataLinkReceiver, DataLinkSender, NetworkInterface}; use std::ffi::CString; use std::fs::File; use std::io; use std::io::Read; use std::mem; use std::path::Path; use std::ptr; use std::slice; use std::sync::Arc; use std::time::Duration; #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] #[repr(C)] struct pollfd { fd: libc::c_int, events: libc::c_short, revents: libc::c_short, } #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] const POLLIN: libc::c_short = 0x0001; #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] const POLLOUT: libc::c_short = 0x0004; #[cfg(target_os = "freebsd")] type nfds_t = libc::c_uint; #[cfg(any(target_os = "linux", target_os = "android"))] type nfds_t = libc::c_ulong; extern "C" { fn ppoll( fds: *mut pollfd, nfds: nfds_t, timeout: *const libc::timespec, newsigmask: *const libc::sigset_t, ) -> libc::c_int; } struct NmDesc { desc: *mut nm_desc, buf_size: libc::c_uint, } unsafe impl Send for NmDesc {} unsafe impl Sync for NmDesc {} impl NmDesc { fn new(iface: &NetworkInterface) -> io::Result { let ifname = CString::new(("netmap:".to_owned() + &iface.name[..]).as_bytes()); let desc = unsafe { nm_open(ifname.unwrap().as_ptr(), ptr::null(), 0, ptr::null()) }; if desc.is_null() { Err(io::Error::last_os_error()) } else { let mut f = try!(File::open(&Path::new( "/sys/module/netmap/parameters/buf_size" ))); let mut num_str = String::new(); try!(f.read_to_string(&mut num_str)); let buf_size = num_str.trim_right().parse().unwrap(); Ok(NmDesc { desc: desc, buf_size: buf_size, }) } } } impl Drop for NmDesc { fn drop(&mut self) { unsafe { nm_close(self.desc); } } } /// The netmap's specific configuration. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Config { /// The read timeout. Defaults to None. pub read_timeout: Option, /// The write timeout. Defaults to None. pub write_timeout: Option, } impl<'a> From<&'a super::Config> for Config { fn from(config: &super::Config) -> Config { Config { read_timeout: config.read_timeout, write_timeout: config.write_timeout, } } } impl Default for Config { fn default() -> Config { Config { read_timeout: None, write_timeout: None, } } } #[inline] fn get_timeout(to: Option) -> Option { to.map(|dur| libc::timespec { tv_sec: dur.as_secs() as libc::time_t, tv_nsec: dur.subsec_nanos() as libc::c_long, }) } /// Create a datalink channel using the netmap library. #[inline] pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result { // FIXME probably want one for each of send/recv let desc = NmDesc::new(network_interface); match desc { Ok(desc) => { let arc = Arc::new(desc); Ok(Ethernet( Box::new(DataLinkSenderImpl { desc: arc.clone(), timeout: get_timeout(config.write_timeout), }), Box::new(DataLinkReceiverImpl { desc: arc, timeout: get_timeout(config.read_timeout), }), )) } Err(e) => Err(e), } } struct DataLinkSenderImpl { desc: Arc, timeout: Option, } impl DataLinkSender for DataLinkSenderImpl { #[inline] fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut FnMut(&mut [u8]), ) -> Option> { assert!(packet_size <= self.desc.buf_size as usize); let desc = self.desc.desc; let mut fds = pollfd { fd: unsafe { NETMAP_FD(desc) }, events: POLLOUT, revents: 0, }; let mut packet_idx = 0usize; while packet_idx < num_packets { unsafe { let timespec = self .timeout .as_ref() .map(|ts| ts as *const _) .unwrap_or(ptr::null()); if ppoll(&mut fds, 1, timespec, ptr::null()) < 0 { return Some(Err(io::Error::last_os_error())); } let ring = NETMAP_TXRING((*desc).nifp, 0); while !nm_ring_empty(ring) && packet_idx < num_packets { let i = (*ring).cur; let slot_ptr: *mut netmap_slot = mem::transmute(&mut (*ring).slot); let buf = NETMAP_BUF(ring, (*slot_ptr.offset(i as isize)).buf_idx as isize); let slice = slice::from_raw_parts_mut(buf as *mut u8, packet_size); (*slot_ptr.offset(i as isize)).len = packet_size as u16; func(slice); let next = nm_ring_next(ring, i); (*ring).head = next; (*ring).cur = next; packet_idx += 1; } } } Some(Ok(())) } #[inline] fn send_to(&mut self, packet: &[u8], _dst: Option) -> Option> { self.build_and_send(1, packet.len(), &mut |eh: &mut [u8]| { eh.clone_from_slice(packet); }) } } struct DataLinkReceiverImpl { desc: Arc, timeout: Option, } impl DataLinkReceiver for DataLinkReceiverImpl { fn next(&mut self) -> io::Result<&[u8]> { let desc = self.desc.desc; let mut h: nm_pkthdr = unsafe { mem::uninitialized() }; let mut buf = unsafe { nm_nextpkt(desc, &mut h) }; if buf.is_null() { let mut fds = pollfd { fd: unsafe { NETMAP_FD(desc) }, events: POLLIN, revents: 0, }; let timespec = self .timeout .as_ref() .map(|ts| ts as *const _) .unwrap_or(ptr::null()); if unsafe { ppoll(&mut fds, 1, timespec, ptr::null()) } < 0 { return Err(io::Error::last_os_error()); } buf = unsafe { nm_nextpkt(desc, &mut h) }; } Ok(unsafe { slice::from_raw_parts(buf, h.len as usize) }) } } /// Get a list of available network interfaces for the current machine. pub fn interfaces() -> Vec { #[path = "unix_interfaces.rs"] mod interfaces; interfaces::interfaces() } pnet_datalink-0.35.0/src/pcap.rs000064400000000000000000000137011046102023000146100ustar 00000000000000//! Support for sending and receiving data link layer packets using libpcap. //! Also has support for reading pcap files. use std::io; use std::marker::{Send, Sync}; use std::path::Path; use std::sync::{Arc, Mutex}; use std::time::Duration; use pcap::{Activated, Active}; use crate::Channel::Ethernet; use crate::{DataLinkReceiver, DataLinkSender, NetworkInterface}; /// Configuration for the pcap datalink backend. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Config { /// The size of buffer to use when reading packets. Must be at least /// 65516 with pcap. pub read_buffer_size: usize, /// The read timeout. Defaults to None. pub read_timeout: Option, /// Promiscuous mode. pub promiscuous: bool, } impl<'a> From<&'a super::Config> for Config { fn from(config: &super::Config) -> Config { let mut c = Config { read_buffer_size: config.read_buffer_size, read_timeout: config.read_timeout, promiscuous: config.promiscuous, }; // pcap is unique in that the buffer size must be greater or equal to // MAXIMUM_SNAPLEN, which is currently hard-coded to 65536 // So, just reset it to the default. if c.read_buffer_size < 65536 { c.read_buffer_size = Config::default().read_buffer_size; } c } } impl Default for Config { fn default() -> Config { Config { // Just let pcap pick the default size read_buffer_size: 0, read_timeout: None, promiscuous: true, } } } /// Create a datalink channel from the provided pcap device. #[inline] pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result { let cap = match pcap::Capture::from_device(&*network_interface.name) { Ok(cap) => cap, Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), } .buffer_size(config.read_buffer_size as i32); // Set pcap timeout (in milliseconds). // For conversion .as_millis() method could be used as well, but might have // a small performance impact as it uses u128 as return type let cap = match config.read_timeout { Some(to) => cap.timeout((to.as_secs() as u32 * 1000 + to.subsec_millis()) as i32), None => cap, }; // Enable promiscuous capture let cap = cap.promisc(config.promiscuous); let cap = match cap.open() { Ok(cap) => cap, Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), }; let cap = Arc::new(Mutex::new(cap)); Ok(Ethernet( Box::new(DataLinkSenderImpl { capture: cap.clone(), }), Box::new(DataLinkReceiverImpl { capture: cap.clone(), read_buffer: vec![0; config.read_buffer_size], }), )) } /// Create a datalink channel from a pcap file. #[inline] pub fn from_file>(path: P, config: Config) -> io::Result { let cap = match pcap::Capture::from_file(path) { Ok(cap) => cap, Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), }; let cap = Arc::new(Mutex::new(cap)); Ok(Ethernet( Box::new(InvalidDataLinkSenderImpl {}), Box::new(DataLinkReceiverImpl { capture: cap.clone(), read_buffer: vec![0; config.read_buffer_size], }), )) } struct DataLinkSenderImpl { capture: Arc>>, } impl DataLinkSender for DataLinkSenderImpl { #[inline] fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut dyn FnMut(&mut [u8]), ) -> Option> { for _ in 0..num_packets { let mut data = vec![0; packet_size]; func(&mut data); let mut cap = self.capture.lock().unwrap(); if let Err(e) = cap.sendpacket(data) { return Some(Err(io::Error::new(io::ErrorKind::Other, e))); } } Some(Ok(())) } #[inline] fn send_to(&mut self, packet: &[u8], _dst: Option) -> Option> { let mut cap = self.capture.lock().unwrap(); Some(match cap.sendpacket(packet) { Ok(()) => Ok(()), Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), }) } } struct InvalidDataLinkSenderImpl {} impl DataLinkSender for InvalidDataLinkSenderImpl { #[inline] fn build_and_send( &mut self, _num_packets: usize, _packet_size: usize, _func: &mut dyn FnMut(&mut [u8]), ) -> Option> { None } #[inline] fn send_to( &mut self, _packet: &[u8], _dst: Option, ) -> Option> { None } } struct DataLinkReceiverImpl { capture: Arc>>, read_buffer: Vec, } impl DataLinkReceiver for DataLinkReceiverImpl { fn next(&mut self) -> io::Result<&[u8]> { let mut cap = self.capture.lock().unwrap(); match cap.next_packet() { Ok(pkt) => { self.read_buffer.truncate(0); self.read_buffer.extend(pkt.data); } Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), }; Ok(&self.read_buffer) } } /// Get a list of available network interfaces for the current machine. pub fn interfaces() -> Vec { if let Ok(devices) = pcap::Device::list() { devices .iter() .enumerate() .map(|(i, dev)| NetworkInterface { name: dev.name.clone(), description: dev.desc.clone().unwrap_or_else(|| "".to_string()), index: i as u32, mac: None, ips: Vec::new(), flags: 0, }) .collect() } else { vec![] } } pnet_datalink-0.35.0/src/unix_interfaces.rs000064400000000000000000000135541046102023000170610ustar 00000000000000// Copyright (c) 2014-2016 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Interface listing implementation for all non-Windows platforms. use crate::{MacAddr, NetworkInterface}; use ipnetwork::{ip_mask_to_prefix, IpNetwork}; use pnet_sys; use libc; use std::ffi::{CStr, CString}; use std::mem; use std::mem::MaybeUninit; use std::net::IpAddr; use std::os::raw::c_char; use std::str::from_utf8_unchecked; /// Get a list of available network interfaces for the current machine. pub fn interfaces() -> Vec { fn merge(old: &mut NetworkInterface, new: &NetworkInterface) { old.mac = match new.mac { None => old.mac, _ => new.mac, }; old.ips.extend_from_slice(&new.ips[..]); old.flags = old.flags | new.flags; } let mut ifaces: Vec = Vec::new(); let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit(); // Safety: addrs.as_mut_ptr() is valid, it points to addrs. if unsafe { libc::getifaddrs(addrs.as_mut_ptr()) } != 0 { return ifaces; } // Safety: If there was an error, we would have already returned. // Therefore, getifaddrs has initialized `addrs`. let addrs = unsafe { addrs.assume_init() }; let mut addr = addrs; while !addr.is_null() { // Safety: We assume that addr is valid for the lifetime of this loop // body, and is not mutated. let addr_ref: &libc::ifaddrs = unsafe {&*addr}; let c_str = addr_ref.ifa_name as *const c_char; // Safety: ifa_name is a null terminated interface name let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() }; // Safety: Interfaces on unix must be valid UTF-8 // TODO: Really? They *must* be UTF-8? let name = unsafe {from_utf8_unchecked(bytes).to_owned() }; let (mac, ip) = sockaddr_to_network_addr(addr_ref.ifa_addr as *const libc::sockaddr); let (_, netmask) = sockaddr_to_network_addr(addr_ref.ifa_netmask as *const libc::sockaddr); let prefix = netmask .and_then(|netmask| ip_mask_to_prefix(netmask).ok()) .unwrap_or(0); let network = ip.and_then(|ip| IpNetwork::new(ip, prefix).ok()); let ni = NetworkInterface { name: name.clone(), description: "".to_string(), index: 0, mac: mac, ips: network.into_iter().collect(), flags: addr_ref.ifa_flags, }; let mut found: bool = false; for iface in &mut ifaces { if name == iface.name { merge(iface, &ni); found = true; } } if !found { ifaces.push(ni); } addr = addr_ref.ifa_next; } // Safety: addrs has been previously allocated through getifaddrs unsafe { libc::freeifaddrs(addrs); } for iface in &mut ifaces { let name = CString::new(iface.name.as_bytes()).unwrap(); // Safety: name.as_ptr() is a valid pointer unsafe { iface.index = libc::if_nametoindex(name.as_ptr()); } } ifaces } #[cfg(any(target_os = "linux", target_os = "android"))] fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { use std::net::SocketAddr; unsafe { if sa.is_null() { (None, None) } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET { let sll: *const libc::sockaddr_ll = mem::transmute(sa); let mac = MacAddr( (*sll).sll_addr[0], (*sll).sll_addr[1], (*sll).sll_addr[2], (*sll).sll_addr[3], (*sll).sll_addr[4], (*sll).sll_addr[5], ); (Some(mac), None) } else { let addr = pnet_sys::sockaddr_to_addr( mem::transmute(sa), mem::size_of::(), ); match addr { Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), Err(_) => (None, None), } } } } #[cfg(any( target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "macos", target_os = "ios", target_os = "tvos" ))] fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { use crate::bindings::bpf; use std::net::SocketAddr; unsafe { if sa.is_null() { (None, None) } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK { let sdl: *const bpf::sockaddr_dl = mem::transmute(sa); let nlen = (*sdl).sdl_nlen as usize; let mac = MacAddr( (*sdl).sdl_data[nlen] as u8, (*sdl).sdl_data[nlen + 1] as u8, (*sdl).sdl_data[nlen + 2] as u8, (*sdl).sdl_data[nlen + 3] as u8, (*sdl).sdl_data[nlen + 4] as u8, (*sdl).sdl_data[nlen + 5] as u8, ); (Some(mac), None) } else { let addr = pnet_sys::sockaddr_to_addr( mem::transmute(sa), mem::size_of::(), ); match addr { Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), Err(_) => (None, None), } } } } pnet_datalink-0.35.0/src/winpcap.rs000064400000000000000000000312551046102023000153320ustar 00000000000000// Copyright (c) 2014-2016 Robert Clipsham // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Support for sending and receiving data link layer packets using the WinPcap library. use super::bindings::{bpf, winpcap}; use super::{DataLinkReceiver, DataLinkSender, MacAddr, NetworkInterface}; use ipnetwork::{ip_mask_to_prefix, IpNetwork}; use std::cmp; use std::collections::VecDeque; use std::ffi::{CStr, CString}; use std::io; use std::mem; use std::slice; use std::str::from_utf8_unchecked; use std::sync::Arc; use winapi::ctypes::c_char; use winapi::ctypes; struct WinPcapAdapter { adapter: winpcap::LPADAPTER, } impl Drop for WinPcapAdapter { fn drop(&mut self) { unsafe { winpcap::PacketCloseAdapter(self.adapter); } } } struct WinPcapPacket { packet: winpcap::LPPACKET, } impl Drop for WinPcapPacket { fn drop(&mut self) { unsafe { winpcap::PacketFreePacket(self.packet); } } } /// The WinPcap's specific configuration. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Config { /// The size of buffer to use when writing packets. Defaults to 4096. pub write_buffer_size: usize, /// The size of buffer to use when reading packets. Defaults to 4096. pub read_buffer_size: usize, } impl<'a> From<&'a super::Config> for Config { fn from(config: &super::Config) -> Config { Config { write_buffer_size: config.write_buffer_size, read_buffer_size: config.read_buffer_size, } } } impl Default for Config { fn default() -> Config { Config { write_buffer_size: 4096, read_buffer_size: 4096, } } } /// Create a datalink channel using the WinPcap library. #[inline] pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result { let mut read_buffer = Vec::new(); read_buffer.resize(config.read_buffer_size, 0u8); let mut write_buffer = Vec::new(); write_buffer.resize(config.write_buffer_size, 0u8); let adapter = unsafe { let net_if_str = CString::new(network_interface.name.as_bytes()).unwrap(); winpcap::PacketOpenAdapter(net_if_str.as_ptr() as *mut ctypes::c_char) }; if adapter.is_null() { return Err(io::Error::last_os_error()); } let ret = unsafe { winpcap::PacketSetHwFilter(adapter, winpcap::NDIS_PACKET_TYPE_PROMISCUOUS) }; if ret == 0 { return Err(io::Error::last_os_error()); } // Set kernel buffer size let ret = unsafe { winpcap::PacketSetBuff(adapter, config.read_buffer_size as ctypes::c_int) }; if ret == 0 { return Err(io::Error::last_os_error()); } // Immediate mode let ret = unsafe { winpcap::PacketSetMinToCopy(adapter, 1) }; if ret == 0 { return Err(io::Error::last_os_error()); } let read_packet = unsafe { winpcap::PacketAllocatePacket() }; if read_packet.is_null() { unsafe { winpcap::PacketCloseAdapter(adapter); } return Err(io::Error::last_os_error()); } unsafe { winpcap::PacketInitPacket( read_packet, read_buffer.as_mut_ptr() as winpcap::PVOID, config.read_buffer_size as winpcap::UINT, ) } let write_packet = unsafe { winpcap::PacketAllocatePacket() }; if write_packet.is_null() { unsafe { winpcap::PacketFreePacket(read_packet); winpcap::PacketCloseAdapter(adapter); } return Err(io::Error::last_os_error()); } unsafe { winpcap::PacketInitPacket( write_packet, write_buffer.as_mut_ptr() as winpcap::PVOID, config.write_buffer_size as winpcap::UINT, ) } let adapter = Arc::new(WinPcapAdapter { adapter: adapter }); let sender = Box::new(DataLinkSenderImpl { adapter: adapter.clone(), _write_buffer: write_buffer, packet: WinPcapPacket { packet: write_packet, }, }); let receiver = Box::new(DataLinkReceiverImpl { adapter: adapter, _read_buffer: read_buffer, packet: WinPcapPacket { packet: read_packet, }, // Enough room for minimally sized packets without reallocating packets: VecDeque::with_capacity(unsafe { (*read_packet).Length } as usize / 64), }); Ok(super::Channel::Ethernet(sender, receiver)) } struct DataLinkSenderImpl { adapter: Arc, _write_buffer: Vec, packet: WinPcapPacket, } impl DataLinkSender for DataLinkSenderImpl { #[inline] fn build_and_send( &mut self, num_packets: usize, packet_size: usize, func: &mut dyn FnMut(&mut [u8]), ) -> Option> { let len = num_packets * packet_size; if len >= unsafe { (*self.packet.packet).Length } as usize { None } else { let min = unsafe { cmp::min((*self.packet.packet).Length as usize, len) }; let slice: &mut [u8] = unsafe { slice::from_raw_parts_mut((*self.packet.packet).Buffer as *mut u8, min) }; for chunk in slice.chunks_mut(packet_size) { func(chunk); // Make sure the right length of packet is sent let old_len = unsafe { (*self.packet.packet).Length }; unsafe { (*self.packet.packet).Length = packet_size as u32; } let ret = unsafe { winpcap::PacketSendPacket(self.adapter.adapter, self.packet.packet, 0) }; unsafe { (*self.packet.packet).Length = old_len; } if ret == 0 { return Some(Err(io::Error::last_os_error())); } } Some(Ok(())) } } #[inline] fn send_to(&mut self, packet: &[u8], _dst: Option) -> Option> { self.build_and_send(1, packet.len(), &mut |eh: &mut [u8]| { eh.copy_from_slice(packet); }) } } unsafe impl Send for DataLinkSenderImpl {} unsafe impl Sync for DataLinkSenderImpl {} struct DataLinkReceiverImpl { adapter: Arc, _read_buffer: Vec, packet: WinPcapPacket, packets: VecDeque<(usize, usize)>, } unsafe impl Send for DataLinkReceiverImpl {} unsafe impl Sync for DataLinkReceiverImpl {} impl DataLinkReceiver for DataLinkReceiverImpl { fn next(&mut self) -> io::Result<&[u8]> { // NOTE Most of the logic here is identical to FreeBSD/OS X while self.packets.is_empty() { let ret = unsafe { winpcap::PacketReceivePacket(self.adapter.adapter, self.packet.packet, 0) }; let buflen = match ret { 0 => return Err(io::Error::last_os_error()), _ => unsafe { (*self.packet.packet).ulBytesReceived as isize }, }; let mut ptr = unsafe { (*self.packet.packet).Buffer as *mut c_char}; let end = unsafe { ((*self.packet.packet).Buffer as *mut c_char).offset(buflen) }; while ptr < end { unsafe { let packet: *const bpf::bpf_hdr = mem::transmute(ptr); let start = ptr as isize + (*packet).bh_hdrlen as isize - (*self.packet.packet).Buffer as isize; self.packets .push_back((start as usize, (*packet).bh_caplen as usize)); let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize; ptr = ptr.offset(bpf::BPF_WORDALIGN(offset)); } } } let (start, len) = self.packets.pop_front().unwrap(); let slice = unsafe { let data = (*self.packet.packet).Buffer as usize + start; slice::from_raw_parts(data as *const u8, len) }; Ok(slice) } } /// Get a list of available network interfaces for the current machine. pub fn interfaces() -> Vec { // use super::bindings::winpcap; let mut adapters_size = 0u32; unsafe { let mut tmp: winpcap::IP_ADAPTER_INFO = mem::zeroed(); // FIXME [windows] This only gets IPv4 addresses - should use // GetAdaptersAddresses winpcap::GetAdaptersInfo(&mut tmp, &mut adapters_size); } let mut vec_size = adapters_size / mem::size_of::() as u32; if adapters_size % mem::size_of::() as u32 != 0 { vec_size += 1; } let mut adapters = Vec::with_capacity(vec_size as usize); // FIXME [windows] Check return code unsafe { winpcap::GetAdaptersInfo(adapters.as_mut_ptr(), &mut adapters_size); } // Create a complete list of NetworkInterfaces for the machine let mut cursor = adapters.as_mut_ptr(); let mut all_ifaces = Vec::with_capacity(vec_size as usize); while !cursor.is_null() { let mac = unsafe { MacAddr( (*cursor).Address[0], (*cursor).Address[1], (*cursor).Address[2], (*cursor).Address[3], (*cursor).Address[4], (*cursor).Address[5], ) }; let mut ip_cursor = unsafe { &mut (*cursor).IpAddressList as winpcap::PIP_ADDR_STRING }; let mut ips = Vec::new(); while !ip_cursor.is_null() { if let Ok(ip_network) = parse_ip_network(ip_cursor) { ips.push(ip_network); } ip_cursor = unsafe { (*ip_cursor).Next }; } unsafe { let name_str_ptr = (*cursor).AdapterName.as_ptr() as *const i8; let bytes = CStr::from_ptr(name_str_ptr).to_bytes(); let name_str = from_utf8_unchecked(bytes).to_owned(); let description_str_ptr = (*cursor).Description.as_ptr() as *const i8; let bytes = CStr::from_ptr(description_str_ptr).to_bytes(); let description_str = from_utf8_unchecked(bytes).to_owned(); all_ifaces.push(NetworkInterface { name: name_str, description: description_str, index: (*cursor).Index, mac: Some(mac), ips: ips, // flags: (*cursor).Type, // FIXME [windows] flags: 0, }); cursor = (*cursor).Next; } } let mut buf = vec![0u8; 4096]; let mut buflen = buf.len() as u32; if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0 { buf.resize(buflen as usize, 0); // Second call should now work with the correct buffer size. If not, this may be // due to some privilege or other unforeseen issue. if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0 { panic!("Unable to get interface list despite increasing buffer size"); } } let buf_str = unsafe { from_utf8_unchecked(&buf) }; let iface_names = buf_str.split("\0\0").next(); let mut vec = Vec::new(); // Return only supported adapters match iface_names { Some(iface_names) => { for iface in iface_names.split('\0') { let name = iface.to_owned(); let next = all_ifaces .iter() .filter(|x| name[..].ends_with(&x.name[..])) .next(); if next.is_some() { let mut iface = next.unwrap().clone(); iface.name = name; vec.push(iface); } } } None => (), }; vec } fn parse_ip_network(ip_cursor: winpcap::PIP_ADDR_STRING) -> Result { let ip_str_ptr = unsafe { &(*ip_cursor) }.IpAddress.String.as_ptr() as *const i8; let ip_bytes = unsafe { CStr::from_ptr(ip_str_ptr).to_bytes() }; let ip_str = unsafe { from_utf8_unchecked(ip_bytes).to_owned() }; let ip = ip_str.parse().map_err(|_| ())?; let mask_str_ptr = unsafe { &(*ip_cursor) }.IpMask.String.as_ptr() as *const i8; let mask_bytes = unsafe { CStr::from_ptr(mask_str_ptr).to_bytes() }; let mask_str = unsafe { from_utf8_unchecked(mask_bytes).to_owned() }; let mask = mask_str.parse().map_err(|_| ())?; let prefix = ip_mask_to_prefix(mask).map_err(|_| ())?; IpNetwork::new(ip, prefix).map_err(|_| ()) }