mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-22 22:33:58 -07:00
Add MPL to utils and other stuff.
This commit is contained in:
parent
5d3536325e
commit
6f9a57740f
24 changed files with 203 additions and 35 deletions
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::io::Write;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
/// Defer execution of a closure until dropped.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
struct Defer<F: FnOnce()>(Option<F>);
|
||||
|
||||
impl<F: FnOnce()> Drop for Defer<F> {
|
||||
|
@ -8,6 +15,9 @@ impl<F: FnOnce()> Drop for Defer<F> {
|
|||
}
|
||||
|
||||
/// Defer execution of a closure until the return value is dropped.
|
||||
///
|
||||
/// This mimics the defer statement in Go, allowing you to always do some cleanup at
|
||||
/// the end of a function no matter where it exits.
|
||||
pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
|
||||
Defer(Some(f))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::Write;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
// These were taken from BSD sysexits.h to provide some standard for process exit codes.
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
|
||||
//use std::sync::atomic::{AtomicI64, Ordering};
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
/// Boolean rate limiter with normal (non-atomic) semantics.
|
||||
#[repr(transparent)]
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::mem::{size_of, MaybeUninit};
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
pub const HEX_CHARS: [u8; 16] = [
|
||||
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f',
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use serde_json::ser::Formatter;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
pub mod arrayvec;
|
||||
pub mod blob;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use std::mem::{needs_drop, size_of, MaybeUninit};
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr::NonNull;
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem::MaybeUninit;
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
/// Variant version of lock for RwLock with automatic conversion to a write lock as needed.
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::mem::{forget, size_of, MaybeUninit};
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::io::{Read, Write};
|
||||
|
||||
|
|
|
@ -23,9 +23,6 @@ pub enum Error {
|
|||
/// There is a safe way to reply if absolutely necessary, by sending the reply back after a constant amount of time, but this is difficult to get correct.
|
||||
FailedAuthentication,
|
||||
|
||||
/// New session was rejected by the application layer.
|
||||
NewSessionRejected,
|
||||
|
||||
/// Rekeying failed and session secret has reached its hard usage count limit
|
||||
MaxKeyLifetimeExceeded,
|
||||
|
||||
|
@ -66,7 +63,6 @@ impl std::fmt::Display for Error {
|
|||
Self::InvalidPacket => f.write_str("InvalidPacket"),
|
||||
Self::InvalidParameter => f.write_str("InvalidParameter"),
|
||||
Self::FailedAuthentication => f.write_str("FailedAuthentication"),
|
||||
Self::NewSessionRejected => f.write_str("NewSessionRejected"),
|
||||
Self::MaxKeyLifetimeExceeded => f.write_str("MaxKeyLifetimeExceeded"),
|
||||
Self::SessionNotEstablished => f.write_str("SessionNotEstablished"),
|
||||
Self::RateLimited => f.write_str("RateLimited"),
|
||||
|
|
|
@ -38,7 +38,7 @@ use crate::sessionid::SessionId;
|
|||
/// Each application using ZSSP must create an instance of this to own sessions and
|
||||
/// defragment incoming packets that are not yet associated with a session.
|
||||
pub struct Context<Application: ApplicationLayer> {
|
||||
initial_offer_defrag: Mutex<RingBufferMap<u64, GatherArray<Application::IncomingPacketBuffer, KEY_EXCHANGE_MAX_FRAGMENTS>, 1024, 256>>,
|
||||
initial_offer_defrag: Mutex<RingBufferMap<u64, GatherArray<Application::IncomingPacketBuffer, KEY_EXCHANGE_MAX_FRAGMENTS>, 1024, 1024>>,
|
||||
sessions: RwLock<SessionMaps<Application>>,
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,9 @@ pub enum ReceiveResult<'b, Application: ApplicationLayer> {
|
|||
|
||||
/// Packet appears valid but was ignored e.g. as a duplicate.
|
||||
Ignored,
|
||||
|
||||
/// Packet appears valid but new session was rejected by application layer.
|
||||
Rejected,
|
||||
}
|
||||
|
||||
/// ZeroTier Secure Session Protocol (ZSSP) Session
|
||||
|
@ -140,10 +143,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Perform periodic background service tasks.
|
||||
/// Perform periodic background service and cleanup tasks.
|
||||
///
|
||||
/// This returns the number of milliseconds until it should be called again. It performs
|
||||
/// tasks like cleaning up internal data structures.
|
||||
/// This returns the number of milliseconds until it should be called again.
|
||||
pub fn service(&self, current_time: i64) -> i64 {
|
||||
let mut dead_active = Vec::new();
|
||||
let mut dead_pending = Vec::new();
|
||||
|
@ -271,16 +273,33 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
|
||||
/// Receive, authenticate, decrypt, and process a physical wire packet.
|
||||
///
|
||||
/// The send function may be called one or more times to send packets. If the packet is associated
|
||||
/// wtth an active session this session is supplied, otherwise this parameter is None. The size
|
||||
/// of packets to be sent will not exceed the supplied mtu.
|
||||
///
|
||||
/// New sessions can be accepted or rejected at both the initial negotiation phase and the final
|
||||
/// negotiation phase using the incoming session filter function. For the initial phase of Noise_XK
|
||||
/// the function will be called with None as a parameter since we do not yet know the static identity
|
||||
/// or meta-data associated with the connection attempt. In the final phase the function will be called
|
||||
/// again with the static public identity blob of the initiating endpoint and optionally any meta-data
|
||||
/// that was supplied. In both cases a return value of false causes abandonment of the session.
|
||||
///
|
||||
/// * `app` - Interface to application using ZSSP
|
||||
/// * `remote_address` - Remote physical address of source endpoint
|
||||
/// * `incoming_session_filter` - Function to call to check whether new sessions should be accepted
|
||||
/// * `send` - Function to call to send packets
|
||||
/// * `data_buf` - Buffer to receive decrypted and authenticated object data (an error is returned if too small)
|
||||
/// * `incoming_packet_buf` - Buffer containing incoming wire packet (receive() takes ownership)
|
||||
/// * `mtu` - Physical wire MTU for sending packets
|
||||
/// * `current_time` - Current monotonic time in milliseconds
|
||||
#[inline]
|
||||
pub fn receive<'b, SendFunction: FnMut(&mut [u8])>(
|
||||
pub fn receive<
|
||||
'b,
|
||||
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
||||
PermitIncomingSession: FnMut(Option<&[u8]>, Option<&[u8]>) -> bool,
|
||||
>(
|
||||
&self,
|
||||
app: &Application,
|
||||
mut incoming_session_filter: PermitIncomingSession,
|
||||
mut send: SendFunction,
|
||||
data_buf: &'b mut [u8],
|
||||
mut incoming_packet_buf: Application::IncomingPacketBuffer,
|
||||
|
@ -311,6 +330,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
return self.receive_complete(
|
||||
app,
|
||||
&mut send,
|
||||
&mut incoming_session_filter,
|
||||
data_buf,
|
||||
counter,
|
||||
assembled_packet.as_ref(),
|
||||
|
@ -332,6 +352,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
return self.receive_complete(
|
||||
app,
|
||||
&mut send,
|
||||
&mut incoming_session_filter,
|
||||
data_buf,
|
||||
counter,
|
||||
&[incoming_packet_buf],
|
||||
|
@ -371,6 +392,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
return self.receive_complete(
|
||||
app,
|
||||
&mut send,
|
||||
&mut incoming_session_filter,
|
||||
data_buf,
|
||||
counter,
|
||||
assembled_packet.as_ref(),
|
||||
|
@ -386,6 +408,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
return self.receive_complete(
|
||||
app,
|
||||
&mut send,
|
||||
&mut incoming_session_filter,
|
||||
data_buf,
|
||||
counter,
|
||||
&[incoming_packet_buf],
|
||||
|
@ -405,10 +428,15 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
///
|
||||
/// NOTE: header check codes will already have been validated on receipt of each fragment. AEAD authentication
|
||||
/// and decryption has NOT yet been performed, and is done here.
|
||||
fn receive_complete<'b, SendFunction: FnMut(&mut [u8])>(
|
||||
fn receive_complete<
|
||||
'b,
|
||||
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
||||
PermitIncomingSession: FnMut(Option<&[u8]>, Option<&[u8]>) -> bool,
|
||||
>(
|
||||
&self,
|
||||
app: &Application,
|
||||
send: &mut SendFunction,
|
||||
incoming_session_filter: &mut PermitIncomingSession,
|
||||
data_buf: &'b mut [u8],
|
||||
counter: u64,
|
||||
fragments: &[Application::IncomingPacketBuffer],
|
||||
|
@ -560,6 +588,10 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
|
||||
if !incoming_session_filter(None, None) {
|
||||
return Ok(ReceiveResult::Rejected);
|
||||
}
|
||||
|
||||
// Decrypt encrypted part of payload (already authenticated above).
|
||||
let mut ctr = AesCtr::new(kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION>(noise_es.as_bytes()).as_bytes());
|
||||
ctr.reset_set_iv(&SHA384::hash(&pkt.alice_noise_e)[..AES_CTR_NONCE_SIZE]);
|
||||
|
@ -649,7 +681,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
reply_buffer[BobNoiseXKAck::AUTH_START..].copy_from_slice(&reply_hmac);
|
||||
|
||||
send_with_fragmentation(
|
||||
send,
|
||||
|b| send(None, b),
|
||||
&mut reply_buffer,
|
||||
mtu,
|
||||
PACKET_TYPE_BOB_NOISE_XK_ACK,
|
||||
|
@ -810,7 +842,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
|||
}
|
||||
|
||||
send_with_fragmentation(
|
||||
send,
|
||||
|b| send(Some(&session), b),
|
||||
&mut reply_buffer[..reply_len],
|
||||
mtu,
|
||||
PACKET_TYPE_ALICE_NOISE_XK_ACK,
|
||||
|
@ -1131,7 +1163,7 @@ fn create_message_nonce(packet_type: u8, counter: u64) -> [u8; AES_GCM_NONCE_SIZ
|
|||
/// Break a packet into fragments and send them all.
|
||||
/// The contents of packet[] are mangled during this operation, so it should be discarded after.
|
||||
fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(
|
||||
send: &mut SendFunction,
|
||||
mut send: SendFunction,
|
||||
packet: &mut [u8],
|
||||
mtu: usize,
|
||||
packet_type: u8,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue