diff --git a/frida_core/Cargo.toml b/frida_core/Cargo.toml
index fe993f8..9507531 100644
--- a/frida_core/Cargo.toml
+++ b/frida_core/Cargo.toml
@@ -44,7 +44,7 @@ wintun = "0.5.0"
 [target.'cfg(target_os="windows")'.build-dependencies]
 embed-resource = "2.3"
 
-[target.'cfg(target_os="darwin")'.dependencies]
+[target.'cfg(target_os="macos")'.dependencies]
 tun-tap = "0.1.4"
 
 [target.'cfg(target_os="linux")'.dependencies]
diff --git a/frida_core/src/lib.rs b/frida_core/src/lib.rs
index 0e4f0e5..be8843a 100644
--- a/frida_core/src/lib.rs
+++ b/frida_core/src/lib.rs
@@ -14,4 +14,10 @@ pub use r#win_tun::*;
 mod linux_tun;
 
 #[cfg(target_os = "linux")]
-pub use r#linux_tun::*;
\ No newline at end of file
+pub use r#linux_tun::*;
+
+#[cfg(target_os = "macos")]
+mod mac_tun;
+
+#[cfg(target_os = "macos")]
+pub use r#mac_tun::*;
\ No newline at end of file
diff --git a/frida_core/src/mac_tun.rs b/frida_core/src/mac_tun.rs
new file mode 100644
index 0000000..b49e902
--- /dev/null
+++ b/frida_core/src/mac_tun.rs
@@ -0,0 +1,51 @@
+use std::process::Command;
+use std::sync::Arc;
+use tun_tap::{Iface, Mode};
+
+use crate::device::AbstractDevice;
+
+fn cmd(cmd: &str, args: &[&str]) {
+    let ecode = Command::new("ip")
+        .args(args)
+        .spawn()
+        .unwrap()
+        .wait()
+        .unwrap();
+    assert!(ecode.success(), "Failed to execte {}", cmd);
+}
+
+pub fn create(cfg: AbstractDevice) -> (DeviceReader, DeviceWriter) {
+    let iface = Iface::new("tun%d", Mode::Tun).unwrap();
+
+    let address = cfg.address.unwrap().to_string();
+    address.push_str("/24");
+
+    cmd("ip", &["addr", "add", "dev", iface.name(), address]);
+    cmd("ip", &["link", "set", "up", "dev", iface.name()]);
+
+    let iface = Arc::new(iface);
+    let writer = Arc::clone(&iface);
+    let reader = Arc::clone(&iface);
+
+    (DeviceReader {reader}, DeviceWriter {writer})
+}
+
+pub struct DeviceWriter {
+    writer: Arc<Iface>
+}
+
+pub struct DeviceReader {
+    reader: Arc<Iface>
+}
+
+impl DeviceWriter {
+    pub async fn write(&self, buf: &Vec<u8>) -> Result<usize, Box<dyn Error>> {
+        self.send(buf)
+    }
+}
+
+impl DeviceReader {
+    pub async fn read(&self, buf: &mut Vec<u8>) -> Result<usize, Box<dyn Error>> {
+        self.recv(buf)
+    }
+}
\ No newline at end of file