Initial commit
This commit is contained in:
commit
ddb1c4d7b0
7 changed files with 801 additions and 0 deletions
29
Formula/fgj-sid.rb
Normal file
29
Formula/fgj-sid.rb
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
class FgjSid < Formula
|
||||
desc "Forgejo/Gitea CLI tool - like gh for GitHub, but for Forgejo and Gitea"
|
||||
homepage "https://forgejo.zerova.net/public/fgj-sid"
|
||||
license "MIT"
|
||||
|
||||
url "ssh://git@forgejo.zerova.net/public/fgj-sid.git",
|
||||
tag: "v0.3.0f",
|
||||
revision: "c2251d9932b9bc3014233f97cfe1c43249a0e22c"
|
||||
head "ssh://git@forgejo.zerova.net/public/fgj-sid.git", branch: "main"
|
||||
|
||||
depends_on "go" => :build
|
||||
|
||||
def install
|
||||
ldflags = "-s -w"
|
||||
system "go", "build", *std_go_args(ldflags:)
|
||||
|
||||
generate_completions_from_executable(bin/"fgj", "completion")
|
||||
|
||||
# Generate man pages
|
||||
mkdir "man" do
|
||||
system bin/"fgj", "manpages", "--dir", "."
|
||||
man1.install Dir["*.1"]
|
||||
end
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match "0.3.0f", shell_output("#{bin}/fgj --version")
|
||||
end
|
||||
end
|
||||
124
Formula/qemu-iscsi.rb
Normal file
124
Formula/qemu-iscsi.rb
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
class QemuIscsi < Formula
|
||||
desc "Generic machine emulator and virtualizer (with libiscsi support)"
|
||||
homepage "https://www.qemu.org/"
|
||||
url "https://download.qemu.org/qemu-10.2.2.tar.xz"
|
||||
sha256 "784b296ff29c1417aa72323abcb2d2ea9ab9771724f577dcd785c3b04f21e176"
|
||||
license "GPL-2.0-only"
|
||||
head "https://gitlab.com/qemu-project/qemu.git", branch: "master"
|
||||
|
||||
livecheck do
|
||||
url "https://www.qemu.org/download/"
|
||||
regex(/href=.*?qemu[._-]v?(\d+(?:\.\d+)+)\.t/i)
|
||||
end
|
||||
|
||||
conflicts_with "qemu", because: "qemu-iscsi installs the same binaries as qemu"
|
||||
|
||||
depends_on "libtool" => :build
|
||||
depends_on "meson" => :build
|
||||
depends_on "ninja" => :build
|
||||
depends_on "pkgconf" => :build
|
||||
depends_on "python@3.14" => :build
|
||||
depends_on "spice-protocol" => :build
|
||||
|
||||
depends_on "capstone"
|
||||
depends_on "dtc"
|
||||
depends_on "glib"
|
||||
depends_on "gnutls"
|
||||
depends_on "jpeg-turbo"
|
||||
depends_on "libiscsi"
|
||||
depends_on "libpng"
|
||||
depends_on "libslirp"
|
||||
depends_on "libssh"
|
||||
depends_on "libusb"
|
||||
depends_on "lzo"
|
||||
depends_on "ncurses"
|
||||
depends_on "pixman"
|
||||
depends_on "snappy"
|
||||
depends_on "vde"
|
||||
depends_on "zstd"
|
||||
|
||||
uses_from_macos "bison" => :build
|
||||
uses_from_macos "flex" => :build
|
||||
uses_from_macos "bzip2"
|
||||
|
||||
on_linux do
|
||||
depends_on "attr"
|
||||
depends_on "cairo"
|
||||
depends_on "elfutils"
|
||||
depends_on "gdk-pixbuf"
|
||||
depends_on "gtk+3"
|
||||
depends_on "keyutils"
|
||||
depends_on "libcap-ng"
|
||||
depends_on "libepoxy"
|
||||
depends_on "libx11"
|
||||
depends_on "libxkbcommon"
|
||||
depends_on "mesa"
|
||||
depends_on "systemd"
|
||||
depends_on "zlib-ng-compat"
|
||||
end
|
||||
|
||||
def install
|
||||
ENV["LIBTOOL"] = "glibtool"
|
||||
|
||||
rm(Dir["python/wheels/*"] - Dir["python/wheels/pycotap-*-none-any.whl"])
|
||||
|
||||
args = %W[
|
||||
--prefix=#{prefix}
|
||||
--cc=#{ENV.cc}
|
||||
--host-cc=#{ENV.cc}
|
||||
--disable-bsd-user
|
||||
--disable-download
|
||||
--disable-guest-agent
|
||||
--enable-slirp
|
||||
--enable-capstone
|
||||
--enable-curses
|
||||
--enable-fdt=system
|
||||
--enable-libiscsi
|
||||
--enable-libssh
|
||||
--enable-vde
|
||||
--enable-virtfs
|
||||
--enable-zstd
|
||||
--extra-cflags=-DNCURSES_WIDECHAR=1
|
||||
--disable-sdl
|
||||
]
|
||||
|
||||
args << "--smbd=#{HOMEBREW_PREFIX}/sbin/samba-dot-org-smbd"
|
||||
|
||||
args += if OS.mac?
|
||||
["--disable-gtk", "--enable-cocoa"]
|
||||
else
|
||||
["--enable-gtk"]
|
||||
end
|
||||
|
||||
system "./configure", *args
|
||||
system "make", "V=1", "install"
|
||||
end
|
||||
|
||||
test do
|
||||
resource "homebrew-test-image" do
|
||||
url "https://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/distributions/1.2/official/FD12FLOPPY.zip"
|
||||
sha256 "81237c7b42dc0ffc8b32a2f5734e3480a3f9a470c50c14a9c4576a2561a35807"
|
||||
end
|
||||
|
||||
archs = %w[
|
||||
aarch64 alpha arm avr hppa i386 loongarch64 m68k microblaze microblazeel mips
|
||||
mips64 mips64el mipsel or1k ppc ppc64 riscv32 riscv64 rx
|
||||
s390x sh4 sh4eb sparc sparc64 tricore x86_64 xtensa xtensaeb
|
||||
]
|
||||
archs.each do |guest_arch|
|
||||
assert_match version.to_s, shell_output("#{bin}/qemu-system-#{guest_arch} --version")
|
||||
end
|
||||
|
||||
resource("homebrew-test-image").stage testpath
|
||||
assert_match "file format: raw", shell_output("#{bin}/qemu-img info FLOPPY.img")
|
||||
|
||||
assert_match "libiscsi",
|
||||
shell_output("#{bin}/qemu-system-x86_64 -drive format=? 2>&1", 1)
|
||||
|
||||
if OS.mac?
|
||||
output = shell_output("codesign --verify --verbose #{bin}/qemu-system-x86_64 2>&1")
|
||||
assert_match "valid on disk", output
|
||||
assert_match "satisfies its Designated Requirement", output
|
||||
end
|
||||
end
|
||||
end
|
||||
83
Formula/tinc-feth.rb
Normal file
83
Formula/tinc-feth.rb
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
class TincFeth < Formula
|
||||
desc "Virtual Private Network (VPN) tool with feth-based TAP support for macOS"
|
||||
homepage "https://www.tinc-vpn.org/"
|
||||
url "https://www.tinc-vpn.org/packages/tinc-1.0.37.tar.gz"
|
||||
sha256 "f63b7e21c32c4c637576d85f36bdd28ea678b5aa17fad02427645dea30e52ac7"
|
||||
license "GPL-2.0-or-later" => { with: "OpenSSL-exception" }
|
||||
|
||||
conflicts_with "tinc", because: "tinc-feth is a patched version of tinc with macOS feth device support"
|
||||
|
||||
depends_on "lzo"
|
||||
depends_on "openssl@3"
|
||||
|
||||
def patches_dir
|
||||
# Resolve patches relative to the tap root
|
||||
File.expand_path("../patches", __dir__)
|
||||
end
|
||||
|
||||
def install
|
||||
# Apply patches: macOS struct ifreq fixes + feth device support
|
||||
system "patch", "-p1", "-i", "#{patches_dir}/net-socket-fix.patch"
|
||||
system "patch", "-p1", "-i", "#{patches_dir}/raw-socket-fix.patch"
|
||||
system "patch", "-p1", "-i", "#{patches_dir}/feth-device.patch"
|
||||
|
||||
system "./configure", "--prefix=#{prefix}",
|
||||
"--sysconfdir=#{etc}",
|
||||
"--with-openssl=#{Formula["openssl@3"].opt_prefix}",
|
||||
"--with-lzo=#{Formula["lzo"].opt_prefix}"
|
||||
system "make", "install"
|
||||
|
||||
# Install wrapper script that auto-detects the network name
|
||||
(bin/"tinc-feth-start").write <<~SH
|
||||
#!/bin/sh
|
||||
TINC_DIR="#{etc}/tinc"
|
||||
if [ -n "$1" ]; then
|
||||
NETWORK="$1"
|
||||
else
|
||||
NETWORK=$(find "$TINC_DIR" -mindepth 1 -maxdepth 1 -type d ! -name '.*' -exec basename {} \\; | head -1)
|
||||
fi
|
||||
if [ -z "$NETWORK" ]; then
|
||||
echo "No tinc network found in $TINC_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
exec #{opt_sbin}/tincd -n "$NETWORK" -D
|
||||
SH
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
tinc-feth adds macOS feth (fake ethernet) device support for Layer 2 / switch mode.
|
||||
|
||||
To use feth mode, set in your tinc.conf:
|
||||
DeviceType = feth
|
||||
Mode = switch
|
||||
|
||||
Optionally specify a device number (default is feth0):
|
||||
Device = feth0
|
||||
|
||||
This creates a feth interface pair:
|
||||
feth0 - primary interface (configure IPs in tinc-up)
|
||||
feth5000 - peer interface (used internally for packet I/O)
|
||||
|
||||
Requires root privileges to create feth interfaces and open BPF devices.
|
||||
|
||||
Example tinc-up script:
|
||||
#!/bin/sh
|
||||
ifconfig $INTERFACE 10.0.0.1 netmask 255.255.255.0 up
|
||||
|
||||
Configuration directory: #{etc}/tinc/
|
||||
EOS
|
||||
end
|
||||
|
||||
service do
|
||||
run [opt_bin/"tinc-feth-start"]
|
||||
require_root true
|
||||
keep_alive true
|
||||
log_path var/"log/tinc/tinc.log"
|
||||
error_log_path var/"log/tinc/tinc.log"
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match version.to_s, shell_output("#{sbin}/tincd --version")
|
||||
end
|
||||
end
|
||||
64
README.md
Normal file
64
README.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Homebrew Tap — sid
|
||||
|
||||
Personal Homebrew tap hosted on [Forgejo](https://forgejo.zerova.net/public/homebrew-sid).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
brew tap public/sid git@forgejo.zerova.net:public/homebrew-sid.git
|
||||
```
|
||||
|
||||
## Available Formulae
|
||||
|
||||
| Formula | Description |
|
||||
|---------|-------------|
|
||||
| [fgj-sid](https://forgejo.zerova.net/public/fgj-sid) | Forgejo/Gitea CLI tool with agentic dev features (fork) |
|
||||
| [tinc-feth](https://forgejo.zerova.net/public/tinc-feth) | tinc 1.0.37 VPN with macOS feth device support |
|
||||
| qemu-iscsi | QEMU built with `--enable-libiscsi` (iSCSI block backend) |
|
||||
|
||||
```bash
|
||||
brew install fgj-sid
|
||||
brew install tinc-feth
|
||||
brew install qemu-iscsi
|
||||
```
|
||||
|
||||
### qemu-iscsi
|
||||
|
||||
QEMU 10.2.2 built identically to `homebrew/core/qemu` but with `libiscsi` as a
|
||||
runtime dependency and `--enable-libiscsi` passed to configure, so
|
||||
`qemu-system-*` can attach `iscsi://` drives directly. Conflicts with the
|
||||
upstream `qemu` formula.
|
||||
|
||||
### tinc-feth
|
||||
|
||||
Patched build of [tinc](https://www.tinc-vpn.org/) 1.0.37 with native macOS feth (fake ethernet) device support for Layer 2 / switch mode. No tuntap kext required.
|
||||
|
||||
```bash
|
||||
# install
|
||||
brew install public/sid/tinc-feth
|
||||
|
||||
# start as service (runs at boot)
|
||||
sudo brew services start public/sid/tinc-feth
|
||||
|
||||
# stop / restart
|
||||
sudo brew services stop public/sid/tinc-feth
|
||||
sudo brew services restart public/sid/tinc-feth
|
||||
|
||||
# manual run with debug
|
||||
sudo /opt/homebrew/opt/tinc-feth/sbin/tincd -n <network> -D -d3
|
||||
```
|
||||
|
||||
Conflicts with the upstream `tinc` formula.
|
||||
|
||||
## Update
|
||||
|
||||
```bash
|
||||
brew upgrade <formula>
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
brew uninstall <formula>
|
||||
brew untap public/sid
|
||||
```
|
||||
437
patches/feth-device.patch
Normal file
437
patches/feth-device.patch
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
--- tinc-1.0.37/src/bsd/device.c 2025-11-08 15:46:26
|
||||
+++ tinc-1.0.37-patched/src/bsd/device.c 2026-03-21 14:13:44
|
||||
@@ -39,6 +39,11 @@
|
||||
#include <net/if_utun.h>
|
||||
#endif
|
||||
|
||||
+#ifdef HAVE_DARWIN
|
||||
+#include <net/bpf.h>
|
||||
+#include <net/ndrv.h>
|
||||
+#endif
|
||||
+
|
||||
#define DEFAULT_TUN_DEVICE "/dev/tun0"
|
||||
#define DEFAULT_TAP_DEVICE "/dev/tap0"
|
||||
|
||||
@@ -50,6 +55,9 @@
|
||||
DEVICE_TYPE_TUNEMU,
|
||||
#endif
|
||||
DEVICE_TYPE_UTUN,
|
||||
+#ifdef HAVE_DARWIN
|
||||
+ DEVICE_TYPE_FETH,
|
||||
+#endif
|
||||
} device_type_t;
|
||||
|
||||
int device_fd = -1;
|
||||
@@ -64,8 +72,302 @@
|
||||
static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD;
|
||||
#else
|
||||
static device_type_t device_type = DEVICE_TYPE_TUN;
|
||||
+#endif
|
||||
+
|
||||
+#ifdef HAVE_DARWIN
|
||||
+/* feth (fake ethernet) device support for macOS 10.13+
|
||||
+ *
|
||||
+ * Uses undocumented feth interfaces (like Linux veth pairs) to provide
|
||||
+ * Layer 2 / TAP functionality without kernel extensions.
|
||||
+ *
|
||||
+ * Architecture:
|
||||
+ * feth<N> (primary) - gets IP assignments, used by tinc-up scripts
|
||||
+ * feth<N+5000> (peer) - used for packet I/O via BPF read + AF_NDRV write
|
||||
+ *
|
||||
+ * Based on ZeroTier's MacEthernetTapAgent approach.
|
||||
+ */
|
||||
+
|
||||
+#define FETH_BPF_BUFSIZE 131072
|
||||
+#define FETH_PEER_OFFSET 5000
|
||||
+#define DEFAULT_FETH_NUM 0
|
||||
+
|
||||
+static int feth_ndrv_fd = -1;
|
||||
+static int feth_num = -1;
|
||||
+static char feth_primary[IFNAMSIZ];
|
||||
+static char feth_peer[IFNAMSIZ];
|
||||
+static uint8_t feth_bpf_buf[FETH_BPF_BUFSIZE];
|
||||
+static int feth_bpf_buf_len = 0;
|
||||
+static int feth_bpf_buf_offset = 0;
|
||||
+
|
||||
+static bool feth_run_cmd(const char *cmd) {
|
||||
+ ifdebug(TRAFFIC) logger(LOG_DEBUG, "Executing: %s", cmd);
|
||||
+
|
||||
+ int ret = system(cmd);
|
||||
+
|
||||
+ if(ret != 0) {
|
||||
+ logger(LOG_ERR, "Command failed (exit %d): %s", ret, cmd);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static bool setup_feth(void) {
|
||||
+ char cmd[512];
|
||||
+
|
||||
+ /* Parse device number from Device config (e.g. "feth0" -> 0) */
|
||||
+ feth_num = DEFAULT_FETH_NUM;
|
||||
+
|
||||
+ if(device) {
|
||||
+ char *p = strstr(device, "feth");
|
||||
+
|
||||
+ if(p) {
|
||||
+ int n = atoi(p + 4);
|
||||
+
|
||||
+ if(n >= 0 && n < FETH_PEER_OFFSET) {
|
||||
+ feth_num = n;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ snprintf(feth_primary, sizeof(feth_primary), "feth%d", feth_num);
|
||||
+ snprintf(feth_peer, sizeof(feth_peer), "feth%d", feth_num + FETH_PEER_OFFSET);
|
||||
+
|
||||
+ /* Create feth pair */
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s create", feth_peer);
|
||||
+
|
||||
+ if(!feth_run_cmd(cmd)) {
|
||||
+ logger(LOG_ERR, "Could not create feth peer interface %s", feth_peer);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ usleep(10000); /* Brief delay for interface to stabilize */
|
||||
+
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s create", feth_primary);
|
||||
+
|
||||
+ if(!feth_run_cmd(cmd)) {
|
||||
+ logger(LOG_ERR, "Could not create feth primary interface %s", feth_primary);
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s destroy 2>/dev/null", feth_peer);
|
||||
+ feth_run_cmd(cmd);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ /* Peer the interfaces */
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s peer %s", feth_peer, feth_primary);
|
||||
+
|
||||
+ if(!feth_run_cmd(cmd)) {
|
||||
+ logger(LOG_ERR, "Could not peer %s with %s", feth_peer, feth_primary);
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* Set MTU */
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s mtu 1500", feth_primary);
|
||||
+ feth_run_cmd(cmd);
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s mtu 1500", feth_peer);
|
||||
+ feth_run_cmd(cmd);
|
||||
+
|
||||
+ /* Bring interfaces up */
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s up", feth_primary);
|
||||
+
|
||||
+ if(!feth_run_cmd(cmd)) {
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s up", feth_peer);
|
||||
+
|
||||
+ if(!feth_run_cmd(cmd)) {
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* Open BPF device */
|
||||
+ char bpf_path[32];
|
||||
+
|
||||
+ for(int i = 1; i < 5000; i++) {
|
||||
+ snprintf(bpf_path, sizeof(bpf_path), "/dev/bpf%d", i);
|
||||
+ device_fd = open(bpf_path, O_RDWR);
|
||||
+
|
||||
+ if(device_fd >= 0) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if(device_fd < 0) {
|
||||
+ logger(LOG_ERR, "Could not open any BPF device: %s", strerror(errno));
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+#ifdef FD_CLOEXEC
|
||||
+ fcntl(device_fd, F_SETFD, FD_CLOEXEC);
|
||||
+#endif
|
||||
+
|
||||
+ /* Configure BPF - order matters: BIOCSBLEN must come before BIOCSETIF */
|
||||
+ int bpf_bufsize = FETH_BPF_BUFSIZE;
|
||||
+
|
||||
+ if(ioctl(device_fd, BIOCSBLEN, &bpf_bufsize) == -1) {
|
||||
+ logger(LOG_ERR, "Could not set BPF buffer size: %s", strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+ struct ifreq ifr;
|
||||
+ memset(&ifr, 0, sizeof(ifr));
|
||||
+ strlcpy(ifr.ifr_name, feth_peer, sizeof(ifr.ifr_name));
|
||||
+
|
||||
+ if(ioctl(device_fd, BIOCSETIF, &ifr) == -1) {
|
||||
+ logger(LOG_ERR, "Could not bind BPF to %s: %s", feth_peer, strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+ int enable = 1;
|
||||
+
|
||||
+ if(ioctl(device_fd, BIOCIMMEDIATE, &enable) == -1) {
|
||||
+ logger(LOG_ERR, "Could not set BIOCIMMEDIATE: %s", strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+ int disable = 0;
|
||||
+
|
||||
+ if(ioctl(device_fd, BIOCSSEESENT, &disable) == -1) {
|
||||
+ logger(LOG_ERR, "Could not set BIOCSSEESENT: %s", strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+ if(ioctl(device_fd, BIOCSHDRCMPLT, &enable) == -1) {
|
||||
+ logger(LOG_ERR, "Could not set BIOCSHDRCMPLT: %s", strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+ if(ioctl(device_fd, BIOCPROMISC, NULL) == -1) {
|
||||
+ logger(LOG_ERR, "Could not set BPF promiscuous mode: %s", strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+ /* Set non-blocking mode for integration with tinc's event loop */
|
||||
+ fcntl(device_fd, F_SETFL, O_NONBLOCK);
|
||||
+
|
||||
+ /* Open AF_NDRV socket for writing packets */
|
||||
+ feth_ndrv_fd = socket(PF_NDRV, SOCK_RAW, 0);
|
||||
+
|
||||
+ if(feth_ndrv_fd < 0) {
|
||||
+ logger(LOG_ERR, "Could not create AF_NDRV socket: %s", strerror(errno));
|
||||
+ goto cleanup_bpf;
|
||||
+ }
|
||||
+
|
||||
+#ifdef FD_CLOEXEC
|
||||
+ fcntl(feth_ndrv_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
+ struct sockaddr_ndrv sa_ndrv;
|
||||
+ memset(&sa_ndrv, 0, sizeof(sa_ndrv));
|
||||
+ sa_ndrv.snd_len = sizeof(sa_ndrv);
|
||||
+ sa_ndrv.snd_family = AF_NDRV;
|
||||
+ strlcpy((char *)sa_ndrv.snd_name, feth_peer, sizeof(sa_ndrv.snd_name));
|
||||
+
|
||||
+ if(bind(feth_ndrv_fd, (struct sockaddr *)&sa_ndrv, sizeof(sa_ndrv)) == -1) {
|
||||
+ logger(LOG_ERR, "Could not bind AF_NDRV socket to %s: %s", feth_peer, strerror(errno));
|
||||
+ goto cleanup_ndrv;
|
||||
+ }
|
||||
+
|
||||
+ if(connect(feth_ndrv_fd, (struct sockaddr *)&sa_ndrv, sizeof(sa_ndrv)) == -1) {
|
||||
+ logger(LOG_ERR, "Could not connect AF_NDRV socket to %s: %s", feth_peer, strerror(errno));
|
||||
+ goto cleanup_ndrv;
|
||||
+ }
|
||||
+
|
||||
+ /* Set interface name for tinc-up scripts */
|
||||
+ iface = xstrdup(feth_primary);
|
||||
+ device_info = "macOS feth device";
|
||||
+
|
||||
+ /* Initialize BPF read buffer state */
|
||||
+ feth_bpf_buf_len = 0;
|
||||
+ feth_bpf_buf_offset = 0;
|
||||
+
|
||||
+ logger(LOG_INFO, "%s is a %s (peer: %s, bpf: %s)", feth_primary, device_info, feth_peer, bpf_path);
|
||||
+
|
||||
+ return true;
|
||||
+
|
||||
+cleanup_ndrv:
|
||||
+ close(feth_ndrv_fd);
|
||||
+ feth_ndrv_fd = -1;
|
||||
+cleanup_bpf:
|
||||
+ close(device_fd);
|
||||
+ device_fd = -1;
|
||||
+cleanup:
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s destroy 2>/dev/null", feth_primary);
|
||||
+ feth_run_cmd(cmd);
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s destroy 2>/dev/null", feth_peer);
|
||||
+ feth_run_cmd(cmd);
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static void close_feth(void) {
|
||||
+ char cmd[256];
|
||||
+
|
||||
+ if(feth_ndrv_fd >= 0) {
|
||||
+ close(feth_ndrv_fd);
|
||||
+ feth_ndrv_fd = -1;
|
||||
+ }
|
||||
+
|
||||
+ if(device_fd >= 0) {
|
||||
+ close(device_fd);
|
||||
+ device_fd = -1;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s destroy 2>/dev/null", feth_primary);
|
||||
+ feth_run_cmd(cmd);
|
||||
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s destroy 2>/dev/null", feth_peer);
|
||||
+ feth_run_cmd(cmd);
|
||||
+
|
||||
+ logger(LOG_INFO, "Destroyed feth pair %s/%s", feth_primary, feth_peer);
|
||||
+}
|
||||
+
|
||||
+static bool read_feth_packet(vpn_packet_t *packet) {
|
||||
+ /* BPF returns multiple packets per read() in a buffer with bpf_hdr prefixes.
|
||||
+ * We maintain a static buffer and return one packet per call. */
|
||||
+
|
||||
+ if(feth_bpf_buf_offset >= feth_bpf_buf_len) {
|
||||
+ /* Buffer exhausted, read more from BPF */
|
||||
+ feth_bpf_buf_len = read(device_fd, feth_bpf_buf, FETH_BPF_BUFSIZE);
|
||||
+
|
||||
+ if(feth_bpf_buf_len <= 0) {
|
||||
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ logger(LOG_ERR, "Error reading from BPF: %s", strerror(errno));
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ feth_bpf_buf_offset = 0;
|
||||
+ }
|
||||
+
|
||||
+ /* Parse next packet from buffer */
|
||||
+ struct bpf_hdr *bh = (struct bpf_hdr *)(feth_bpf_buf + feth_bpf_buf_offset);
|
||||
+ uint8_t *frame = feth_bpf_buf + feth_bpf_buf_offset + bh->bh_hdrlen;
|
||||
+ int caplen = bh->bh_caplen;
|
||||
+
|
||||
+ /* Advance offset to next packet (word-aligned) */
|
||||
+ feth_bpf_buf_offset += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
|
||||
+
|
||||
+ if(caplen <= 0 || caplen > MTU) {
|
||||
+ ifdebug(TRAFFIC) logger(LOG_DEBUG, "Dropping oversized feth packet (%d bytes)", caplen);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ memcpy(packet->data, frame, caplen);
|
||||
+ packet->len = caplen;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static bool write_feth_packet(vpn_packet_t *packet) {
|
||||
+ if(write(feth_ndrv_fd, packet->data, packet->len) < 0) {
|
||||
+ logger(LOG_ERR, "Error writing to feth AF_NDRV: %s", strerror(errno));
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+#endif /* HAVE_DARWIN */
|
||||
+
|
||||
#ifdef HAVE_NET_IF_UTUN_H
|
||||
static bool setup_utun(void) {
|
||||
device_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
|
||||
@@ -154,6 +456,12 @@
|
||||
}
|
||||
|
||||
#endif
|
||||
+#ifdef HAVE_DARWIN
|
||||
+ else if(!strcasecmp(type, "feth")) {
|
||||
+ device_type = DEVICE_TYPE_FETH;
|
||||
+ }
|
||||
+
|
||||
+#endif
|
||||
else if(!strcasecmp(type, "tunnohead")) {
|
||||
device_type = DEVICE_TYPE_TUN;
|
||||
} else if(!strcasecmp(type, "tunifhead")) {
|
||||
@@ -169,6 +477,12 @@
|
||||
|
||||
if(strncmp(device, "utun", 4) == 0 || strncmp(device, "/dev/utun", 9) == 0) {
|
||||
device_type = DEVICE_TYPE_UTUN;
|
||||
+ } else
|
||||
+#endif
|
||||
+#ifdef HAVE_DARWIN
|
||||
+
|
||||
+ if(strncmp(device, "feth", 4) == 0) {
|
||||
+ device_type = DEVICE_TYPE_FETH;
|
||||
} else
|
||||
#endif
|
||||
if(strstr(device, "tap") || routing_mode != RMODE_ROUTER) {
|
||||
@@ -176,11 +490,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
+#ifdef HAVE_DARWIN
|
||||
+
|
||||
+ if(routing_mode == RMODE_SWITCH && device_type != DEVICE_TYPE_TAP && device_type != DEVICE_TYPE_FETH) {
|
||||
+ logger(LOG_ERR, "Only tap or feth devices support switch mode!");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+#else
|
||||
+
|
||||
if(routing_mode == RMODE_SWITCH && device_type != DEVICE_TYPE_TAP) {
|
||||
logger(LOG_ERR, "Only tap devices support switch mode!");
|
||||
return false;
|
||||
}
|
||||
|
||||
+#endif
|
||||
+
|
||||
// Open the device
|
||||
|
||||
switch(device_type) {
|
||||
@@ -197,7 +522,12 @@
|
||||
case DEVICE_TYPE_UTUN:
|
||||
return setup_utun();
|
||||
#endif
|
||||
+#ifdef HAVE_DARWIN
|
||||
|
||||
+ case DEVICE_TYPE_FETH:
|
||||
+ return setup_feth();
|
||||
+#endif
|
||||
+
|
||||
default:
|
||||
device_fd = open(device, O_RDWR | O_NONBLOCK);
|
||||
}
|
||||
@@ -336,7 +666,13 @@
|
||||
tunemu_close(device_fd);
|
||||
break;
|
||||
#endif
|
||||
+#ifdef HAVE_DARWIN
|
||||
|
||||
+ case DEVICE_TYPE_FETH:
|
||||
+ close_feth();
|
||||
+ break;
|
||||
+#endif
|
||||
+
|
||||
default:
|
||||
close(device_fd);
|
||||
}
|
||||
@@ -427,6 +763,16 @@
|
||||
packet->len = lenin;
|
||||
break;
|
||||
|
||||
+#ifdef HAVE_DARWIN
|
||||
+
|
||||
+ case DEVICE_TYPE_FETH:
|
||||
+ if(!read_feth_packet(packet)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ break;
|
||||
+#endif
|
||||
+
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -506,6 +852,16 @@
|
||||
break;
|
||||
#endif
|
||||
|
||||
+#ifdef HAVE_DARWIN
|
||||
+
|
||||
+ case DEVICE_TYPE_FETH:
|
||||
+ if(!write_feth_packet(packet)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ break;
|
||||
+#endif
|
||||
+
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
37
patches/net-socket-fix.patch
Normal file
37
patches/net-socket-fix.patch
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
--- tinc-1.0.37/src/net_socket.c 2025-11-08 15:46:26
|
||||
+++ tinc-1.0.37-patched/src/net_socket.c 2026-03-21 14:12:58
|
||||
@@ -102,14 +102,14 @@
|
||||
|
||||
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
- strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
|
||||
- ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
|
||||
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
|
||||
+ ifr.ifr_name[IFNAMSIZ - 1] = 0;
|
||||
free(iface);
|
||||
|
||||
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
|
||||
|
||||
if(status) {
|
||||
- logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(errno));
|
||||
+ logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -157,13 +157,13 @@
|
||||
struct ifreq ifr;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
- strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
|
||||
- ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
|
||||
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
|
||||
+ ifr.ifr_name[IFNAMSIZ - 1] = 0;
|
||||
free(iface);
|
||||
|
||||
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) {
|
||||
closesocket(nfd);
|
||||
- logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(sockerrno));
|
||||
+ logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_name, strerror(sockerrno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
27
patches/raw-socket-fix.patch
Normal file
27
patches/raw-socket-fix.patch
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
--- tinc-1.0.37/src/raw_socket_device.c 2025-11-08 15:46:26
|
||||
+++ tinc-1.0.37-patched/src/raw_socket_device.c 2026-03-21 14:13:18
|
||||
@@ -61,12 +61,12 @@
|
||||
#endif
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
- strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
|
||||
- ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
|
||||
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
|
||||
+ ifr.ifr_name[IFNAMSIZ - 1] = 0;
|
||||
|
||||
if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
|
||||
close(device_fd);
|
||||
- logger(LOG_ERR, "Can't find interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(errno));
|
||||
+ logger(LOG_ERR, "Can't find interface %s: %s", ifr.ifr_name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
sa.sll_ifindex = ifr.ifr_ifindex;
|
||||
|
||||
if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) {
|
||||
- logger(LOG_ERR, "Could not bind %s to %s: %s", device, ifr.ifr_ifrn.ifrn_name, strerror(errno));
|
||||
+ logger(LOG_ERR, "Could not bind %s to %s: %s", device, ifr.ifr_name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue