1use binrw::{NullWideString, io::TakeSeekExt, prelude::*};
3use modular_bitfield::prelude::*;
4use smb_dtyp::binrw_util::prelude::*;
5
6use crate::{Dialect, NegotiateSecurityMode};
7
8use crate::dfsc::{ReqGetDfsReferral, ReqGetDfsReferralEx, RespGetDfsReferral};
9use smb_dtyp::*;
10use smb_fscc::*;
11
12use super::common::IoctlRequestContent;
13use crate::IoctlBuffer;
14use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
15use std::ops::{Deref, DerefMut};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u32)]
19pub enum FsctlCodes {
20 DfsGetReferrals = 0x00060194,
21 OffloadRead = 0x00094264,
22 PipePeek = 0x0011400C,
23 PipeWait = 0x00110018,
24 PipeTransceive = 0x0011C017,
25 SrvCopychunk = 0x001440F2,
26 SrvEnumerateSnapshots = 0x00144064,
27 SrvRequestResumeKey = 0x00140078,
28 SrvReadHash = 0x001441bb,
29 SrvCopychunkWrite = 0x001480F2,
30 LmrRequestResiliency = 0x001401D4,
31 QueryNetworkInterfaceInfo = 0x001401FC,
32 SetReparsePoint = 0x000900A4,
33 DfsGetReferralsEx = 0x000601B0,
34 FileLevelTrim = 0x00098208,
35 ValidateNegotiateInfo = 0x00140204,
36 QueryAllocatedRanges = 0x000940CF,
37}
38
39#[binrw::binrw]
42#[derive(Debug, PartialEq, Eq)]
43pub struct SrvCopychunkCopy {
44 pub source_key: [u8; SrvCopychunkCopy::SRV_KEY_LENGTH],
45 #[bw(try_calc = chunks.len().try_into())]
46 chunk_count: u32,
47 #[bw(calc = 0)]
48 _reserved: u32,
49 #[br(count = chunk_count)]
50 pub chunks: Vec<SrvCopychunkItem>,
51}
52
53impl SrvCopychunkCopy {
54 pub const SRV_KEY_LENGTH: usize = 24;
55 pub const SIZE: usize = Self::SRV_KEY_LENGTH + 4 + 4;
56}
57
58#[binrw::binrw]
59#[derive(Debug, PartialEq, Eq)]
60pub struct SrvCopychunkItem {
61 pub source_offset: u64,
62 pub target_offset: u64,
63 pub length: u32,
64 #[bw(calc = 0)]
65 _reserved: u32,
66}
67
68impl SrvCopychunkItem {
69 pub const SIZE: usize = size_of::<u64>() * 2 + size_of::<u32>() * 2;
70}
71
72impl IoctlRequestContent for SrvCopychunkCopy {
73 fn get_bin_size(&self) -> u32 {
74 (Self::SIZE + self.chunks.len() * SrvCopychunkItem::SIZE) as u32
75 }
76}
77
78#[binrw::binrw]
79#[derive(Debug, PartialEq, Eq)]
80pub struct SrvReadHashReq {
81 #[bw(calc = 1)]
83 #[br(assert(hash_type == 1))]
84 pub hash_type: u32,
85 #[br(assert((1..=2).contains(&hash_version)))]
87 #[bw(assert((1..=2).contains(hash_version)))]
88 pub hash_version: u32,
89 pub hash_retrieval_type: SrvHashRetrievalType,
90}
91
92impl IoctlRequestContent for SrvReadHashReq {
93 fn get_bin_size(&self) -> u32 {
94 size_of::<u32>() as u32 * 3
95 }
96}
97
98#[binrw::binrw]
99#[derive(Debug, PartialEq, Eq)]
100#[brw(repr(u32))]
101pub enum SrvHashRetrievalType {
102 HashBased = 1,
103 FileBased = 2,
104}
105
106#[binrw::binrw]
108#[derive(Debug, PartialEq, Eq)]
109pub struct NetworkResiliencyRequest {
110 pub timeout: u32,
112 #[bw(calc = 0)]
113 pub _reserved: u32,
114}
115
116impl IoctlRequestContent for NetworkResiliencyRequest {
117 fn get_bin_size(&self) -> u32 {
118 size_of::<u32>() as u32 * 2
119 }
120}
121
122#[binrw::binrw]
123#[derive(Debug, PartialEq, Eq)]
124pub struct ValidateNegotiateInfoRequest {
125 pub capabilities: u32,
126 pub guid: Guid,
127 pub security_mode: NegotiateSecurityMode,
128 #[bw(try_calc = dialects.len().try_into())]
129 dialect_count: u16,
130 #[br(count = dialect_count)]
131 pub dialects: Vec<Dialect>,
132}
133
134impl IoctlRequestContent for ValidateNegotiateInfoRequest {
135 fn get_bin_size(&self) -> u32 {
136 (size_of::<u32>()
137 + Guid::GUID_SIZE
138 + 2
139 + size_of::<u16>()
140 + self.dialects.len() * size_of::<u16>()) as u32
141 }
142}
143
144#[binrw::binrw]
145#[derive(Debug, PartialEq, Eq)]
146pub struct SrvSnapshotArray {
147 pub number_of_snap_shots: u32,
148 pub number_of_snap_shots_returned: u32,
149 #[bw(calc = PosMarker::default())]
150 pub snap_shot_array_size: PosMarker<u32>,
151 #[br(parse_with = binrw::helpers::until_eof, map_stream = |s| s.take_seek(snap_shot_array_size.value as u64))]
152 #[bw(write_with = PosMarker::write_size, args(&snap_shot_array_size))]
153 pub snap_shots: Vec<NullWideString>,
154}
155
156pub trait FsctlResponseContent: for<'a> BinRead<Args<'a> = ()> + std::fmt::Debug {
158 const FSCTL_CODES: &'static [FsctlCodes];
159}
160
161macro_rules! impl_fsctl_response {
162 ($code:ident, $type:ty) => {
163 impl FsctlResponseContent for $type {
164 const FSCTL_CODES: &'static [FsctlCodes] = &[FsctlCodes::$code];
165 }
166 };
167}
168
169#[binrw::binrw]
170#[derive(Debug, PartialEq, Eq)]
171pub struct SrvRequestResumeKey {
172 pub resume_key: [u8; SrvCopychunkCopy::SRV_KEY_LENGTH],
173 #[bw(calc = 0)]
174 context_length: u32,
175 #[br(count = context_length)]
177 #[bw(assert(context.len() == context_length as usize))]
178 pub context: Vec<u8>,
179}
180
181impl_fsctl_response!(SrvRequestResumeKey, SrvRequestResumeKey);
182
183#[binrw::binrw]
184#[derive(Debug, PartialEq, Eq)]
185pub struct SrvCopychunkResponse {
186 pub chunks_written: u32,
187 pub chunk_bytes_written: u32,
188 pub total_bytes_written: u32,
189}
190
191impl_fsctl_response!(SrvCopychunk, SrvCopychunkResponse);
192
193#[binrw::binrw]
194#[derive(Debug, PartialEq, Eq)]
195pub struct SrvReadHashRes {
196 #[bw(calc = 1)]
198 #[br(assert(hash_type == 1))]
199 hash_type: u32,
200 #[br(assert((1..=2).contains(&hash_version)))]
202 #[bw(assert((1..=2).contains(hash_version)))]
203 hash_version: u32,
204 source_file_change_time: FileTime,
205 source_file_size: u64,
206 hash_blob_length: PosMarker<u32>,
207 hash_blob_offset: PosMarker<u32>,
208 dirty: u16,
209 #[bw(try_calc = source_file_name.len().try_into())]
210 source_file_name_length: u16,
211 #[br(count = source_file_name_length)]
212 source_file_name: Vec<u8>,
213}
214
215impl_fsctl_response!(SrvReadHash, SrvReadHashRes);
216
217#[binrw::binrw]
218#[derive(Debug, PartialEq, Eq)]
219pub struct SrvHashRetrieveHashBased {
220 pub offset: u64,
221 #[bw(try_calc = blob.len().try_into())]
222 buffer_length: u32,
223 #[bw(calc = 0)]
224 _reserved: u32,
225 #[br(count = buffer_length)]
227 blob: Vec<u8>,
228}
229
230impl_fsctl_response!(SrvReadHash, SrvHashRetrieveHashBased);
231
232#[binrw::binrw]
233#[derive(Debug, PartialEq, Eq)]
234pub struct SrvHashRetrieveFileBased {
235 pub file_data_offset: u64,
236 pub file_data_length: u64,
237 #[bw(try_calc = buffer.len().try_into())]
238 buffer_length: u32,
239 #[bw(calc = 0)]
240 _reserved: u32,
241 #[br(count = buffer_length)]
243 pub buffer: Vec<u8>,
244}
245
246pub type NetworkInterfacesInfo = ChainedItemList<NetworkInterfaceInfo>;
247
248impl_fsctl_response!(QueryNetworkInterfaceInfo, NetworkInterfacesInfo);
249
250#[binrw::binrw]
251#[derive(Debug, PartialEq, Eq)]
252pub struct NetworkInterfaceInfo {
253 pub if_index: u32,
254 pub capability: NetworkInterfaceCapability,
255 #[bw(calc = 0)]
256 _reserved: u32,
257 pub link_speed: u64,
258 pub sockaddr: SocketAddrStorage,
260}
261
262#[bitfield]
263#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
264#[bw(map = |&x| Self::into_bytes(x))]
265#[br(map = Self::from_bytes)]
266pub struct NetworkInterfaceCapability {
267 pub rss: bool,
268 pub rdma: bool,
269 #[skip]
270 __: B30,
271}
272
273#[binrw::binrw]
274#[derive(Debug, PartialEq, Eq)]
275pub enum SocketAddrStorage {
276 V4(SocketAddrStorageV4),
277 V6(SocketAddrStorageV6),
278}
279
280impl SocketAddrStorage {
281 pub fn socket_addr(&self) -> SocketAddr {
282 match self {
283 SocketAddrStorage::V4(v4) => SocketAddr::V4(v4.to_addr()),
284 SocketAddrStorage::V6(v6) => SocketAddr::V6(v6.to_addr()),
285 }
286 }
287}
288
289#[binrw::binrw]
290#[derive(Debug, PartialEq, Eq)]
291#[brw(magic(b"\x02\x00"))] pub struct SocketAddrStorageV4 {
293 pub port: u16,
294 pub address: u32,
295 #[bw(calc = [0; 128 - (2 + 2 + 4)])]
296 _reserved: [u8; 128 - (2 + 2 + 4)],
297}
298
299impl SocketAddrStorageV4 {
300 fn to_addr(&self) -> SocketAddrV4 {
301 SocketAddrV4::new(Ipv4Addr::from(self.address.to_be()), self.port)
302 }
303}
304
305#[binrw::binrw]
306#[derive(Debug, PartialEq, Eq)]
307#[brw(magic(b"\x17\x00"))] pub struct SocketAddrStorageV6 {
309 pub port: u16,
310 pub flow_info: u32,
311 pub address: u128,
312 pub scope_id: u32,
313 #[bw(calc = [0; 128 - (2 + 2 + 4 + 16 + 4)])]
314 _reserved: [u8; 128 - (2 + 2 + 4 + 16 + 4)],
315}
316
317impl SocketAddrStorageV6 {
318 fn to_addr(&self) -> SocketAddrV6 {
319 SocketAddrV6::new(
320 Ipv6Addr::from(self.address.to_be()),
321 self.port,
322 self.flow_info,
323 self.scope_id,
324 )
325 }
326}
327
328#[binrw::binrw]
329#[derive(Debug, PartialEq, Eq)]
330pub struct ValidateNegotiateInfoResponse {
331 pub capabilities: u32,
332 pub guid: Guid,
333 pub security_mode: NegotiateSecurityMode,
334 pub dialect: Dialect,
335}
336
337impl_fsctl_response!(ValidateNegotiateInfo, ValidateNegotiateInfoResponse);
338
339impl FsctlResponseContent for RespGetDfsReferral {
341 const FSCTL_CODES: &'static [FsctlCodes] =
342 &[FsctlCodes::DfsGetReferrals, FsctlCodes::DfsGetReferralsEx];
343}
344
345impl IoctlRequestContent for ReqGetDfsReferral {
346 fn get_bin_size(&self) -> u32 {
347 (size_of::<u16>() + (self.request_file_name.len() + 1) * size_of::<u16>()) as u32
348 }
349}
350
351impl IoctlRequestContent for ReqGetDfsReferralEx {
352 fn get_bin_size(&self) -> u32 {
353 (size_of::<u16>() * 2 + size_of::<u32>() + self.request_data.get_bin_size()) as u32
354 }
355}
356
357#[binrw::binrw]
358#[derive(Debug, PartialEq, Eq)]
359pub struct QueryAllocRangesItem {
360 pub offset: u64,
361 pub len: u64,
362}
363
364impl IoctlRequestContent for QueryAllocRangesItem {
365 fn get_bin_size(&self) -> u32 {
366 (size_of::<u64>() * 2) as u32
367 }
368}
369
370#[binrw::binrw]
371#[derive(Debug, Default, PartialEq, Eq)]
372pub struct QueryAllocRangesResult {
373 #[br(parse_with = binrw::helpers::until_eof)]
374 values: Vec<QueryAllocRangesItem>,
375}
376
377impl Deref for QueryAllocRangesResult {
378 type Target = Vec<QueryAllocRangesItem>;
379 fn deref(&self) -> &Self::Target {
380 &self.values
381 }
382}
383
384impl From<Vec<QueryAllocRangesItem>> for QueryAllocRangesResult {
385 fn from(value: Vec<QueryAllocRangesItem>) -> Self {
386 Self { values: value }
387 }
388}
389
390impl_fsctl_response!(QueryAllocatedRanges, QueryAllocRangesResult);
391
392#[binrw::binrw]
397#[derive(Debug, PartialEq, Eq)]
398pub struct PipeWaitRequest {
399 pub timeout: u64,
402 #[bw(calc = name.size() as u32)]
403 name_length: u32,
404 pub timeout_specified: Boolean,
407 #[bw(calc = 0)]
408 _padding: u8,
409 #[br(args {size: SizedStringSize::bytes(name_length)})]
412 pub name: SizedWideString,
413}
414
415impl IoctlRequestContent for PipeWaitRequest {
416 fn get_bin_size(&self) -> u32 {
417 (size_of::<u64>()
418 + size_of::<u32>()
419 + size_of::<Boolean>()
420 + size_of::<u8>()
421 + self.name.size() as usize) as u32
422 }
423}
424
425#[binrw::binrw]
429#[derive(Debug, PartialEq, Eq)]
430pub struct SetReparsePointRequest {
431 #[bw(assert((reparse_tag & 0x80000000 == 0) == reparse_guid.is_some()))]
433 pub reparse_tag: u32,
434 #[bw(calc = reparse_data.len() as u32)]
435 reparse_data_length: u32,
436 #[br(if(reparse_tag & 0x80000000 == 0))]
440 pub reparse_guid: Option<Guid>,
441 #[br(count = reparse_data_length)]
443 pub reparse_data: Vec<u8>,
444}
445
446impl IoctlRequestContent for SetReparsePointRequest {
447 fn get_bin_size(&self) -> u32 {
448 (size_of::<u32>()
449 + size_of::<u32>()
450 + self.reparse_guid.as_ref().map_or(0, |_| size_of::<Guid>())
451 + self.reparse_data.len()) as u32
452 }
453}
454
455#[binrw::binrw]
456#[derive(Debug, PartialEq, Eq)]
457pub struct FileLevelTrimRequest {
458 #[bw(calc = 0)]
460 _key: u32,
461 #[bw(calc = ranges.len() as u32)]
462 num_ranges: u32,
463 #[br(count = num_ranges)]
465 pub ranges: Vec<FileLevelTrimRange>,
466}
467
468#[binrw::binrw]
472#[derive(Debug, PartialEq, Eq)]
473pub struct FileLevelTrimRange {
474 pub offset: u64,
476 pub length: u64,
478}
479
480impl IoctlRequestContent for FileLevelTrimRequest {
481 fn get_bin_size(&self) -> u32 {
482 (size_of::<u32>() + size_of::<u32>() + self.ranges.len() * size_of::<FileLevelTrimRange>())
483 as u32
484 }
485}
486
487#[binrw::binrw]
489#[derive(Debug, PartialEq, Eq)]
490pub struct PipePeekResponse {
491 pub named_pipe_state: NamedPipeState,
493 #[bw(calc = data.len() as u32)]
494 read_data_available: u32,
496 pub number_of_messages: u32,
498 pub message_length: u32,
500 #[br(count = read_data_available as u64)]
502 pub data: Vec<u8>,
503}
504
505impl_fsctl_response!(PipePeek, PipePeekResponse);
506
507#[binrw::binrw]
509#[derive(Debug, PartialEq, Eq)]
510pub struct SrvEnumerateSnapshotsResponse {
511 pub number_of_snap_shots: u32,
513 pub number_of_snap_shots_returned: u32,
517 #[bw(calc = PosMarker::default())]
519 snap_shot_array_size: PosMarker<u32>,
520 #[br(map_stream = |s| s.take_seek(snap_shot_array_size.value as u64))]
522 #[bw(write_with = PosMarker::write_size, args(&snap_shot_array_size))]
523 pub snap_shots: MultiWSz,
524}
525
526impl_fsctl_response!(SrvEnumerateSnapshots, SrvEnumerateSnapshotsResponse);
527
528#[binrw::binrw]
530#[derive(Debug, PartialEq, Eq)]
531pub struct FileLevelTrimResponse {
532 pub num_ranges_processed: u32,
534}
535
536impl_fsctl_response!(FileLevelTrim, FileLevelTrimResponse);
537
538#[binrw::binrw]
540#[derive(Debug, PartialEq, Eq)]
541pub struct OffloadReadRequest {
542 #[bw(calc = 0x20)]
543 #[br(assert(_size == 0x20))]
544 _size: u32,
545 pub flags: u32,
547 pub token_time_to_live: u32,
549 #[bw(calc = 0)]
550 _reserved: u32,
551 pub file_offset: u64,
554 pub copy_length: u64,
557}
558
559impl IoctlRequestContent for OffloadReadRequest {
560 fn get_bin_size(&self) -> u32 {
561 (size_of::<u32>() * 4 + size_of::<u64>() * 2) as u32
562 }
563}
564
565#[binrw::binrw]
567#[derive(Debug, PartialEq, Eq)]
568pub struct OffloadReadResponse {
569 #[bw(calc = 528)]
570 #[br(assert(_size == 528))]
571 _size: u32,
572
573 pub all_zero_beyond_current_range: Boolean,
576 _padding: u8,
577 _padding2: u16,
578
579 pub transfer_length: u64,
584
585 pub token: [u8; 512], }
589
590impl_fsctl_response!(OffloadRead, OffloadReadResponse);
591
592macro_rules! make_newtype {
598 ($vis:vis $name:ident($inner:ty)) => {
599 #[binrw::binrw]
600 #[derive(Debug, PartialEq, Eq)]
601 pub struct $name(pub $inner);
602
603 impl $name {
604 pub fn new(inner: $inner) -> Self {
605 Self(inner)
606 }
607 }
608
609 impl From<$inner> for $name {
610 fn from(inner: $inner) -> Self {
611 Self(inner)
612 }
613 }
614
615 impl Deref for $name {
616 type Target = $inner;
617
618 fn deref(&self) -> &Self::Target {
619 &self.0
620 }
621 }
622
623 impl DerefMut for $name {
624 fn deref_mut(&mut self) -> &mut Self::Target {
625 &mut self.0
626 }
627 }
628 };
629}
630
631macro_rules! make_req_newtype {
632 ($vis:vis $name:ident($inner:ty)) => {
633 make_newtype!($vis $name($inner));
634 impl IoctlRequestContent for $name {
635 fn get_bin_size(&self) -> u32 {
636 self.0.get_bin_size()
637 }
638 }
639 }
640}
641
642macro_rules! make_res_newtype {
643 ($fsctl:ident: $vis:vis $name:ident($inner:ty)) => {
644 make_newtype!($vis $name($inner));
645 impl FsctlResponseContent for $name {
646 const FSCTL_CODES: &'static [FsctlCodes] = &[FsctlCodes::$fsctl];
647 }
648 }
649}
650
651make_req_newtype!(pub PipePeekRequest(()));
652make_req_newtype!(pub SrvEnumerateSnapshotsRequest(()));
653make_req_newtype!(pub SrvRequestResumeKeyRequest(()));
654make_req_newtype!(pub QueryNetworkInterfaceInfoRequest(()));
655make_req_newtype!(pub PipeTransceiveRequest(IoctlBuffer));
656make_req_newtype!(pub SrvCopyChunkCopyWrite(SrvCopychunkCopy));
657
658make_res_newtype!(
659 PipeWait: pub PipeWaitResponse(())
660);
661make_res_newtype!(
662 PipeTransceive: pub PipeTransceiveResponse(IoctlBuffer)
663);
664make_res_newtype!(
665 SetReparsePoint: pub SetReparsePointResponse(())
666);
667
668make_res_newtype!(
669 LmrRequestResiliency: pub LmrRequestResiliencyResponse(())
670);
671
672#[cfg(test)]
673mod tests {
674 use super::*;
675 use smb_tests::*;
676
677 test_binrw! {
678 struct OffloadReadRequest {
679 flags: 0,
680 token_time_to_live: 0,
681 file_offset: 0,
682 copy_length: 10485760,
683 } => "2000000000000000000000000000000000000000000000000000a00000000000"
684 }
685
686 test_binrw! {
687 struct SrvRequestResumeKey {
688 resume_key: [
689 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2,
690 0xdb, 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
691 ],
692 context: vec![],
693 } => "2d0300001c00000027116a2630d2db01fffe00000000000000000000"
694 }
695
696 const CHUNK_SIZE: u32 = 1 << 20; const TOTAL_SIZE: u32 = 10417096;
698 const BLOCK_NUM: u32 = (TOTAL_SIZE + CHUNK_SIZE - 1) / CHUNK_SIZE;
699
700 test_binrw! {
701 struct SrvCopychunkCopy {
702 source_key: [
703 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2, 0xdb,
704 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
705 ],
706 chunks: (0..BLOCK_NUM).map(|i| SrvCopychunkItem {
707 source_offset: (i * CHUNK_SIZE) as u64,
708 target_offset: (i * CHUNK_SIZE) as u64,
709 length: if i == BLOCK_NUM - 1 {
710 TOTAL_SIZE % CHUNK_SIZE
711 } else {
712 CHUNK_SIZE
713 },
714 }).collect(),
715 } => "2d0300001c00000027116a2630d2db01fffe0000000000000a000000000000000
716 00000000000000000000000000000000000100000000000000010000000000000001000
717 00000000000010000000000000002000000000000000200000000000000010000000000
718 00000300000000000000030000000000000001000000000000000400000000000000040
719 00000000000000100000000000000050000000000000005000000000000000100000000
720 00000006000000000000000600000000000000010000000000000007000000000000000
721 70000000000000001000000000000000800000000000000080000000000000001000000
722 0000000009000000000000000900000000000c8f30e0000000000"
723 }
724
725 test_binrw! {
726 struct SrvCopychunkResponse {
727 chunks_written: 10,
728 chunk_bytes_written: 0,
729 total_bytes_written: 10417096,
730 } => "0a00000000000000c8f39e00"
731 }
732
733 test_binrw! {
734 struct QueryAllocRangesResult {
735 values: vec![
736 QueryAllocRangesItem {
737 offset: 0,
738 len: 4096,
739 },
740 QueryAllocRangesItem {
741 offset: 8192,
742 len: 46801,
743 },
744 ],
745 } => "000000000000000000100000000000000020000000000000d1b6000000000000"
746 }
747
748 test_binrw! {
749 NetworkInterfacesInfo: NetworkInterfacesInfo::from(vec![
750 NetworkInterfaceInfo {
751 if_index: 2,
752 capability: NetworkInterfaceCapability::new().with_rdma(true),
753 link_speed: 1000000000,
754 sockaddr: SocketAddrStorage::V4(SocketAddrStorageV4 {
755 port: 0,
756 address: 0xac10cc84u32.to_be(),
757 })
758 },
759 NetworkInterfaceInfo {
760 if_index: 2,
761 capability: NetworkInterfaceCapability::new().with_rdma(true),
762 link_speed: 1000000000,
763 sockaddr: SocketAddrStorage::V6(SocketAddrStorageV6 {
764 port: 0,
765 flow_info: 0,
766 address: 0xfe80000000000000020c29fffe9f8bf3u128.to_be(),
767 scope_id: 0,
768 })
769 },
770 ]) => "9800000002000000020000000000000000ca9a3b0000000002000000ac10cc8400000000000000
771 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
772 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
773 0000000000000000000000000000000000000000000000000000000000000000020000000200000000000
774 00000ca9a3b000000001700000000000000fe80000000000000020c29fffe9f8bf3000000000000000000
775 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
776 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
777 00000000000000000000"
778 }
779
780 }