//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` //! if needed but no point in adding extra public API when there is not Std support for UEFI in the //! first place //! //! Some Nomenclature //! * Protocol: //! - Protocols serve to enable communication between separately built modules, including drivers. //! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. //! - Protocols are produced and consumed. //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; use crate::marker::PhantomData; use crate::mem::MaybeUninit; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys_common::wstr::WStrUnits; type BootInstallMultipleProtocolInterfaces = unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; type BootUninstallMultipleProtocolInterfaces = unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; const BOOT_SERVICES_UNAVAILABLE: io::Error = const_error!(io::ErrorKind::Other, "Boot Services are no longer available"); /// Locates Handles with a particular Protocol GUID. /// /// Implemented using `EFI_BOOT_SERVICES.LocateHandles()`. /// /// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { fn inner( guid: &mut Guid, boot_services: NonNull, buf_size: &mut usize, buf: *mut r_efi::efi::Handle, ) -> io::Result<()> { let r = unsafe { ((*boot_services.as_ptr()).locate_handle)( r_efi::efi::BY_PROTOCOL, guid, crate::ptr::null_mut(), buf_size, buf, ) }; if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let mut buf_len = 0usize; // This should always fail since the size of buffer is 0. This call should update the buf_len // variable with the required buffer length match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { Ok(()) => unreachable!(), Err(e) => match e.kind() { io::ErrorKind::FileTooLarge => {} _ => return Err(e), }, } // The returned buf_len is in bytes assert_eq!(buf_len % size_of::(), 0); let num_of_handles = buf_len / size_of::(); let mut buf: Vec = Vec::with_capacity(num_of_handles); match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { Ok(()) => { // This is safe because the call will succeed only if buf_len >= required length. // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written unsafe { buf.set_len(num_of_handles) }; Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) } Err(e) => Err(e), } } /// Open Protocol on a handle. /// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. /// /// Queries a handle to determine if it supports a specified protocol. If the protocol is /// supported by the handle, it opens the protocol on behalf of the calling agent. /// /// The protocol is opened with the attribute GET_PROTOCOL, which means the caller is not required /// to close the protocol interface with `EFI_BOOT_SERVICES.CloseProtocol()` pub(crate) fn open_protocol( handle: NonNull, mut protocol_guid: Guid, ) -> io::Result> { let boot_services: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let system_handle = uefi::env::image_handle(); let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); let r = unsafe { ((*boot_services.as_ptr()).open_protocol)( handle.as_ptr(), &mut protocol_guid, protocol.as_mut_ptr().cast(), system_handle.as_ptr(), crate::ptr::null_mut(), r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, ) }; if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(unsafe { protocol.assume_init() }) .ok_or(const_error!(io::ErrorKind::Other, "null protocol")) } } /// Gets the Protocol for current system handle. /// /// Note: Some protocols need to be manually freed. It is the caller's responsibility to do so. pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> io::Result> { let system_handle = uefi::env::try_image_handle() .ok_or(io::const_error!(io::ErrorKind::NotFound, "protocol not found in Image handle"))?; open_protocol(system_handle, protocol_guid) } pub(crate) fn device_path_to_text(path: NonNull) -> io::Result { fn path_to_text( protocol: NonNull, path: NonNull, ) -> io::Result { let path_ptr: *mut r_efi::efi::Char16 = unsafe { ((*protocol.as_ptr()).convert_device_path_to_text)( path.as_ptr(), // DisplayOnly r_efi::efi::Boolean::FALSE, // AllowShortcuts r_efi::efi::Boolean::FALSE, ) }; let path = os_string_from_raw(path_ptr) .ok_or(io::const_error!(io::ErrorKind::InvalidData, "invalid path"))?; if let Some(boot_services) = crate::os::uefi::env::boot_services() { let boot_services: NonNull = boot_services.cast(); unsafe { ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); } } Ok(path) } static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { if let Ok(protocol) = open_protocol::( handle, device_path_to_text::PROTOCOL_GUID, ) { return path_to_text(protocol, path); } } let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; for handle in device_path_to_text_handles { if let Ok(protocol) = open_protocol::( handle, device_path_to_text::PROTOCOL_GUID, ) { LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); return path_to_text(protocol, path); } } Err(io::const_error!(io::ErrorKind::NotFound, "no device path to text protocol found")) } fn device_node_to_text(path: NonNull) -> io::Result { fn node_to_text( protocol: NonNull, path: NonNull, ) -> io::Result { let path_ptr: *mut r_efi::efi::Char16 = unsafe { ((*protocol.as_ptr()).convert_device_node_to_text)( path.as_ptr(), // DisplayOnly r_efi::efi::Boolean::FALSE, // AllowShortcuts r_efi::efi::Boolean::FALSE, ) }; let path = os_string_from_raw(path_ptr) .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; if let Some(boot_services) = crate::os::uefi::env::boot_services() { let boot_services: NonNull = boot_services.cast(); unsafe { ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); } } Ok(path) } static LAST_VALID_HANDLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { if let Ok(protocol) = open_protocol::( handle, device_path_to_text::PROTOCOL_GUID, ) { return node_to_text(protocol, path); } } let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; for handle in device_path_to_text_handles { if let Ok(protocol) = open_protocol::( handle, device_path_to_text::PROTOCOL_GUID, ) { LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); return node_to_text(protocol, path); } } Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Gets RuntimeServices. pub(crate) fn runtime_services() -> Option> { let system_table: NonNull = crate::os::uefi::env::try_system_table()?.cast(); let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services }; NonNull::new(runtime_services) } pub(crate) struct OwnedDevicePath(NonNull); impl OwnedDevicePath { pub(crate) fn from_text(p: &OsStr) -> io::Result { fn inner( p: &OsStr, protocol: NonNull, ) -> io::Result { let path_vec = p.encode_wide().chain(Some(0)).collect::>(); if path_vec[..path_vec.len() - 1].contains(&0) { return Err(const_error!( io::ErrorKind::InvalidInput, "strings passed to UEFI cannot contain NULs", )); } let path = unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; NonNull::new(path) .map(OwnedDevicePath) .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "invalid Device Path")) } static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { if let Ok(protocol) = open_protocol::( handle, r_efi::protocols::device_path_from_text::PROTOCOL_GUID, ) { return inner(p, protocol); } } let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?; for handle in handles { if let Ok(protocol) = open_protocol::( handle, r_efi::protocols::device_path_from_text::PROTOCOL_GUID, ) { LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); return inner(p, protocol); } } io::Result::Err(const_error!( io::ErrorKind::NotFound, "DevicePathFromText Protocol not found", )) } pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> { BorrowedDevicePath::new(self.0) } } impl Drop for OwnedDevicePath { fn drop(&mut self) { if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); unsafe { ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void); } } } } impl crate::fmt::Debug for OwnedDevicePath { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { match self.borrow().to_text() { Ok(p) => p.fmt(f), Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), } } } pub(crate) struct BorrowedDevicePath<'a> { protocol: NonNull, phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, } impl<'a> BorrowedDevicePath<'a> { pub(crate) const fn new(protocol: NonNull) -> Self { Self { protocol, phantom: PhantomData } } pub(crate) fn to_text(&self) -> io::Result { device_path_to_text(self.protocol) } pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> { DevicePathIterator::new(DevicePathNode::new(self.protocol)) } } impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { match self.to_text() { Ok(p) => p.fmt(f), Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(), } } } pub(crate) struct DevicePathIterator<'a>(Option>); impl<'a> DevicePathIterator<'a> { const fn new(node: DevicePathNode<'a>) -> Self { if node.is_end() { Self(None) } else { Self(Some(node)) } } } impl<'a> Iterator for DevicePathIterator<'a> { type Item = DevicePathNode<'a>; fn next(&mut self) -> Option { let cur_node = self.0?; let next_node = unsafe { cur_node.next_node() }; self.0 = if next_node.is_end() { None } else { Some(next_node) }; Some(cur_node) } } #[derive(Copy, Clone)] pub(crate) struct DevicePathNode<'a> { protocol: NonNull, phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, } impl<'a> DevicePathNode<'a> { pub(crate) const fn new(protocol: NonNull) -> Self { Self { protocol, phantom: PhantomData } } pub(crate) const fn length(&self) -> u16 { let len = unsafe { (*self.protocol.as_ptr()).length }; u16::from_le_bytes(len) } pub(crate) const fn node_type(&self) -> u8 { unsafe { (*self.protocol.as_ptr()).r#type } } pub(crate) const fn sub_type(&self) -> u8 { unsafe { (*self.protocol.as_ptr()).sub_type } } pub(crate) fn data(&self) -> &[u8] { let length: usize = self.length().into(); // Some nodes do not have any special data if length > 4 { let raw_ptr: *const u8 = self.protocol.as_ptr().cast(); let data = unsafe { raw_ptr.add(4) }; unsafe { crate::slice::from_raw_parts(data, length - 4) } } else { &[] } } pub(crate) const fn is_end(&self) -> bool { self.node_type() == r_efi::protocols::device_path::TYPE_END && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE } pub(crate) const fn is_end_instance(&self) -> bool { self.node_type() == r_efi::protocols::device_path::TYPE_END && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE } pub(crate) unsafe fn next_node(&self) -> Self { let node = unsafe { self.protocol .cast::() .add(self.length().into()) .cast::() }; Self::new(node) } pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> { BorrowedDevicePath::new(self.protocol) } pub(crate) fn to_text(&self) -> io::Result { device_node_to_text(self.protocol) } } impl<'a> PartialEq for DevicePathNode<'a> { fn eq(&self, other: &Self) -> bool { // Compare as a single buffer rather than by field since it optimizes better. // // SAFETY: `Protocol` is followed by a buffer of `length - sizeof::()`. `Protocol` // has no padding so it is sound to interpret as a slice. unsafe { let s1 = slice::from_raw_parts(self.protocol.as_ptr().cast::(), self.length().into()); let s2 = slice::from_raw_parts(other.protocol.as_ptr().cast::(), other.length().into()); s1 == s2 } } } impl<'a> crate::fmt::Debug for DevicePathNode<'a> { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { match self.to_text() { Ok(p) => p.fmt(f), Err(_) => f .debug_struct("DevicePathNode") .field("type", &self.node_type()) .field("sub_type", &self.sub_type()) .field("length", &self.length()) .field("specific_device_path_data", &self.data()) .finish(), } } } /// Protocols installed by Rust side on a handle. pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, protocol: *mut T, } impl OwnedProtocol { // FIXME: Consider using unsafe trait for matching protocol with guid pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result { let bt: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let protocol: *mut T = Box::into_raw(Box::new(protocol)); let mut handle: r_efi::efi::Handle = crate::ptr::null_mut(); // FIXME: Move into r-efi once extended_varargs_abi_support is stabilized let func: BootInstallMultipleProtocolInterfaces = unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) }; let r = unsafe { func( &mut handle, &mut guid as *mut _ as *mut crate::ffi::c_void, protocol as *mut crate::ffi::c_void, crate::ptr::null_mut() as *mut crate::ffi::c_void, ) }; if r.is_error() { drop(unsafe { Box::from_raw(protocol) }); return Err(crate::io::Error::from_raw_os_error(r.as_usize())); }; let handle = NonNull::new(handle) .ok_or(io::const_error!(io::ErrorKind::Uncategorized, "found null handle"))?; Ok(Self { guid, handle, protocol }) } pub(crate) fn handle(&self) -> NonNull { self.handle } } impl Drop for OwnedProtocol { fn drop(&mut self) { // Do not deallocate a runtime protocol if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); // FIXME: Move into r-efi once extended_varargs_abi_support is stabilized let func: BootUninstallMultipleProtocolInterfaces = unsafe { crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces) }; let status = unsafe { func( self.handle.as_ptr(), &mut self.guid as *mut _ as *mut crate::ffi::c_void, self.protocol as *mut crate::ffi::c_void, crate::ptr::null_mut() as *mut crate::ffi::c_void, ) }; // Leak the protocol in case uninstall fails if status == r_efi::efi::Status::SUCCESS { let _ = unsafe { Box::from_raw(self.protocol) }; } } } } impl AsRef for OwnedProtocol { fn as_ref(&self) -> &T { unsafe { self.protocol.as_ref().unwrap() } } } pub(crate) struct OwnedTable { layout: crate::alloc::Layout, ptr: *mut T, } impl OwnedTable { pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self { let header_size = hdr.header_size as usize; let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap(); let ptr = unsafe { crate::alloc::alloc(layout) as *mut T }; Self { layout, ptr } } pub(crate) const fn as_ptr(&self) -> *const T { self.ptr } pub(crate) const fn as_mut_ptr(&self) -> *mut T { self.ptr } } impl OwnedTable { pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self { let hdr = unsafe { (*tbl).hdr }; let owned_tbl = Self::from_table_header(&hdr); unsafe { crate::ptr::copy_nonoverlapping( tbl as *const u8, owned_tbl.as_mut_ptr() as *mut u8, hdr.header_size as usize, ) }; owned_tbl } } impl Drop for OwnedTable { fn drop(&mut self) { unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) }; } } /// Create OsString from a pointer to NULL terminated UTF-16 string pub(crate) fn os_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option { let path_len = unsafe { WStrUnits::new(ptr)?.count() }; Some(OsString::from_wide(unsafe { slice::from_raw_parts(ptr.cast(), path_len) })) } /// Create NULL terminated UTF-16 string pub(crate) fn os_string_to_raw(s: &OsStr) -> Option> { let temp = s.encode_wide().chain(Some(0)).collect::>(); if temp[..temp.len() - 1].contains(&0) { None } else { Some(temp) } } pub(crate) fn open_shell() -> Option> { static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { if let Ok(protocol) = open_protocol::(handle, shell::PROTOCOL_GUID) { return Some(protocol); } } let handles = locate_handles(shell::PROTOCOL_GUID).ok()?; for handle in handles { if let Ok(protocol) = open_protocol::(handle, shell::PROTOCOL_GUID) { LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); return Some(protocol); } } None } /// Get device path protocol associated with shell mapping. /// /// returns None in case no such mapping is exists pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result> { let shell = open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?; let mut path = os_string_to_raw(map.as_os_str()) .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "invalid UEFI shell mapping"))?; // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not // freed throughout it's lifetime. So it has a 'static lifetime. let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) }; let protocol = NonNull::new(protocol) .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?; Ok(BorrowedDevicePath::new(protocol)) } /// Helper for UEFI Protocols which are created and destroyed using /// [EFI_SERVICE_BINDING_PROTOCOL](https://uefi.org/specs/UEFI/2.11/11_Protocols_UEFI_Driver_Model.html#efi-service-binding-protocol) pub(crate) struct ServiceProtocol { service_guid: r_efi::efi::Guid, handle: NonNull, child_handle: NonNull, } impl ServiceProtocol { pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result { let handles = locate_handles(service_guid)?; for handle in handles { if let Ok(protocol) = open_protocol::(handle, service_guid) { let Ok(child_handle) = Self::create_child(protocol) else { continue; }; return Ok(Self { service_guid, handle, child_handle }); } } Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) } pub(crate) fn child_handle(&self) -> NonNull { self.child_handle } fn create_child( sbp: NonNull, ) -> io::Result> { let mut child_handle: r_efi::efi::Handle = crate::ptr::null_mut(); // SAFETY: A new handle is allocated if a pointer to NULL is passed. let r = unsafe { ((*sbp.as_ptr()).create_child)(sbp.as_ptr(), &mut child_handle) }; if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(child_handle) .ok_or(const_error!(io::ErrorKind::Other, "null child handle")) } } } impl Drop for ServiceProtocol { fn drop(&mut self) { if let Ok(sbp) = open_protocol::(self.handle, self.service_guid) { // SAFETY: Child handle must be allocated by the current service binding protocol. let _ = unsafe { ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), self.child_handle.as_ptr()) }; } } } #[repr(transparent)] pub(crate) struct OwnedEvent(NonNull); impl OwnedEvent { pub(crate) fn new( signal: u32, tpl: efi::Tpl, handler: Option, context: Option>, ) -> io::Result { let boot_services: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let mut event: r_efi::efi::Event = crate::ptr::null_mut(); let context = context.map(NonNull::as_ptr).unwrap_or(crate::ptr::null_mut()); let r = unsafe { let create_event = (*boot_services.as_ptr()).create_event; (create_event)(signal, tpl, handler, context, &mut event) }; if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(event) .ok_or(const_error!(io::ErrorKind::Other, "failed to create event")) .map(Self) } } pub(crate) fn as_ptr(&self) -> efi::Event { self.0.as_ptr() } pub(crate) fn into_raw(self) -> *mut crate::ffi::c_void { let r = self.0.as_ptr(); crate::mem::forget(self); r } /// SAFETY: Assumes that ptr is a non-null valid UEFI event pub(crate) unsafe fn from_raw(ptr: *mut crate::ffi::c_void) -> Self { Self(unsafe { NonNull::new_unchecked(ptr) }) } } impl Drop for OwnedEvent { fn drop(&mut self) { if let Some(boot_services) = boot_services() { let bt: NonNull = boot_services.cast(); unsafe { let close_event = (*bt.as_ptr()).close_event; (close_event)(self.0.as_ptr()) }; } } } pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Address { efi::Ipv4Address { addr: addr.octets() } } pub(crate) const fn ipv4_from_r_efi(ip: efi::Ipv4Address) -> crate::net::Ipv4Addr { crate::net::Ipv4Addr::new(ip.addr[0], ip.addr[1], ip.addr[2], ip.addr[3]) }