.
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Tue, 21 Oct 2025 02:59:27 +0000 (02:59 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Tue, 21 Oct 2025 02:59:27 +0000 (02:59 +0000)
developer/.gitignore [deleted file]
developer/cc/Rabbit_2017_count.kmod.c [new file with mode: 0644]
developer/cc/Rabbit_2017_to_WG.kmod.c [new file with mode: 0644]
developer/cc/Rabbit_no-op.kmod.c [new file with mode: 0644]
developer/scratchpad/.gitignore [new file with mode: 0644]
developer/tool/makefile
document/conventions/release_howto.org [deleted file]
document/quick_start_developer.sh [new file with mode: 0644]
release/kmod/Rabbit_2017_count.ko [new file with mode: 0644]

diff --git a/developer/.gitignore b/developer/.gitignore
deleted file mode 100644 (file)
index 120f485..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!/.gitignore
diff --git a/developer/cc/Rabbit_2017_count.kmod.c b/developer/cc/Rabbit_2017_count.kmod.c
new file mode 100644 (file)
index 0000000..ff4801b
--- /dev/null
@@ -0,0 +1,81 @@
+// rabbit_uid2017.c — Count v4/v6 LOCAL_OUT & POST_ROUTING packets from UID 2017 only.
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/atomic.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <net/sock.h>
+
+/* Hardcoded target UID */
+#define RABBIT_UID 2017
+
+/* Counters: only increment when packet is from UID 2017 */
+static atomic64_t cnt_v4_local_out  = ATOMIC64_INIT(0);
+static atomic64_t cnt_v4_postroute  = ATOMIC64_INIT(0);
+static atomic64_t cnt_v6_local_out  = ATOMIC64_INIT(0);
+static atomic64_t cnt_v6_postroute  = ATOMIC64_INIT(0);
+
+static inline bool from_uid_2017(const struct nf_hook_state *st, struct sk_buff *skb) {
+  struct sock *sk = st->sk ? st->sk : skb_to_full_sk(skb);
+  if (!sk) return false;                       /* no socket context */
+  return __kuid_val(sock_i_uid(sk)) == RABBIT_UID;
+}
+
+static unsigned int rabbit_v4_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st) {
+  if (!from_uid_2017(st, skb)) return NF_ACCEPT;
+
+  if (st->hook == NF_INET_LOCAL_OUT)          atomic64_inc(&cnt_v4_local_out);
+  else if (st->hook == NF_INET_POST_ROUTING)  atomic64_inc(&cnt_v4_postroute);
+  return NF_ACCEPT;
+}
+
+static unsigned int rabbit_v6_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st) {
+  if (!from_uid_2017(st, skb)) return NF_ACCEPT;
+
+  if (st->hook == NF_INET_LOCAL_OUT)          atomic64_inc(&cnt_v6_local_out);
+  else if (st->hook == NF_INET_POST_ROUTING)  atomic64_inc(&cnt_v6_postroute);
+  return NF_ACCEPT;
+}
+
+static struct nf_hook_ops rabbit_ops[] = {
+  { .hook = rabbit_v4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT,    .priority = NF_IP_PRI_FIRST },
+  { .hook = rabbit_v4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_FIRST },
+  { .hook = rabbit_v6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT,    .priority = NF_IP6_PRI_FIRST },
+  { .hook = rabbit_v6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_FIRST },
+};
+
+static int __init rabbit_init(void) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+  int ret = nf_register_net_hooks(&init_net, rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#else
+  int ret = nf_register_hooks(rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#endif
+  if (ret) pr_err("rabbit_uid2017: nf_register_* failed: %d\n", ret);
+  else     pr_info("rabbit_uid2017: loaded; counting UID %d only (no-op)\n", RABBIT_UID);
+  return ret;
+}
+
+static void __exit rabbit_exit(void) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+  nf_unregister_net_hooks(&init_net, rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#else
+  nf_unregister_hooks(rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#endif
+  pr_info("rabbit_uid2017: unload stats v4(lo=%lld,po=%lld) v6(lo=%lld,po=%lld) [UID=%d]\n",
+          (long long)atomic64_read(&cnt_v4_local_out),
+          (long long)atomic64_read(&cnt_v4_postroute),
+          (long long)atomic64_read(&cnt_v6_local_out),
+          (long long)atomic64_read(&cnt_v6_postroute),
+          RABBIT_UID);
+}
+
+module_init(rabbit_init);
+module_exit(rabbit_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rabbit");
+MODULE_DESCRIPTION("Count LOCAL_OUT/POST_ROUTING packets from UID 2017");
diff --git a/developer/cc/Rabbit_2017_to_WG.kmod.c b/developer/cc/Rabbit_2017_to_WG.kmod.c
new file mode 100644 (file)
index 0000000..cb2ed99
--- /dev/null
@@ -0,0 +1,106 @@
+// rabbit_2017_to_WG.c — forward UID 2017 traffic to WG
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/atomic.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <net/sock.h>
+
+#define RABBIT_UID 2017
+#define DEV_NAME   "US"   /* WireGuard iface to force */
+
+static atomic64_t cnt_v4_local_out = ATOMIC64_INIT(0);
+static atomic64_t cnt_v4_postroute = ATOMIC64_INIT(0);
+
+struct v4_cidr { __be32 net, mask; };
+#define V4(x) (__force __be32)cpu_to_be32(x)
+static struct v4_cidr v4_excepts[] = {
+  { V4(0x7f000000), V4(0xff000000) }, /* 127.0.0.0/8 */
+  { V4(0xa9fe0000), V4(0xffff0000) }, /* 169.254.0.0/16 */
+  { V4(0xc0a80000), V4(0xffff0000) }, /* 192.168.0.0/16  (adjust) */
+  { V4(0x0a000002), V4(0xffffffff) }, /* 10.0.0.2/32    US local */
+  { V4(0x0a080004), V4(0xffffffff) }, /* 10.8.0.4/32    x6 local */
+};
+static inline bool v4_in_cidr(__be32 a, struct v4_cidr c){ return (a & c.mask) == (c.net & c.mask); }
+static bool v4_is_except(__be32 d){ int i; for (i=0;i<ARRAY_SIZE(v4_excepts);i++) if (v4_in_cidr(d, v4_excepts[i])) return true; return false; }
+
+static int us_ifindex;  /* cached ifindex for DEV_NAME */
+
+static inline bool from_uid_2017(const struct nf_hook_state *st, struct sk_buff *skb){
+  struct sock *sk = st->sk ? st->sk : skb_to_full_sk(skb);
+  if (!sk) return false;
+  return __kuid_val(sock_i_uid(sk)) == RABBIT_UID;
+}
+
+/* Bind the socket to DEV_NAME once we see its first packet */
+static inline void maybe_bind_socket_to_us(struct sk_buff *skb, const struct iphdr *iph, const struct nf_hook_state *st){
+  struct sock *sk = st->sk ? st->sk : skb_to_full_sk(skb);
+  if (!sk || !us_ifindex) return;
+  if (v4_is_except(iph->daddr)) return;
+  if (READ_ONCE(sk->sk_bound_dev_if) == us_ifindex) return;
+  WRITE_ONCE(sk->sk_bound_dev_if, us_ifindex);
+}
+
+static unsigned int rabbit_v4_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st){
+  if (st->pf != NFPROTO_IPV4) return NF_ACCEPT;
+
+  if (st->hook == NF_INET_LOCAL_OUT) {
+    if (from_uid_2017(st, skb)) {
+      const struct iphdr *iph = ip_hdr(skb);
+      if (iph) maybe_bind_socket_to_us(skb, iph, st);
+      atomic64_inc(&cnt_v4_local_out);
+    }
+  } else if (st->hook == NF_INET_POST_ROUTING) {
+    if (from_uid_2017(st, skb)) atomic64_inc(&cnt_v4_postroute);
+  }
+  return NF_ACCEPT;
+}
+
+static struct nf_hook_ops rabbit_ops[] = {
+  { .hook = rabbit_v4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT,    .priority = NF_IP_PRI_FIRST },
+  { .hook = rabbit_v4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_FIRST },
+};
+
+static int __init rabbit_init(void){
+  struct net_device *dev;
+  rcu_read_lock();
+  dev = dev_get_by_name_rcu(&init_net, DEV_NAME);
+  us_ifindex = dev ? dev->ifindex : 0;
+  rcu_read_unlock();
+  if (!us_ifindex) {
+    pr_err("rabbit_uid2017_bind: device \"%s\" not found\n", DEV_NAME);
+    return -ENODEV;
+  }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+  {
+    int ret = nf_register_net_hooks(&init_net, rabbit_ops, ARRAY_SIZE(rabbit_ops));
+    if (ret) return ret;
+  }
+#else
+  { int ret = nf_register_hooks(rabbit_ops, ARRAY_SIZE(rabbit_ops)); if (ret) return ret; }
+#endif
+  pr_info("rabbit_uid2017_bind: loaded; UID=%d bound to dev %s(ifindex=%d)\n", RABBIT_UID, DEV_NAME, us_ifindex);
+  return 0;
+}
+
+static void __exit rabbit_exit(void){
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+  nf_unregister_net_hooks(&init_net, rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#else
+  nf_unregister_hooks(rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#endif
+  pr_info("rabbit_uid2017_bind: unload v4(lo=%lld,po=%lld)\n",
+          (long long)atomic64_read(&cnt_v4_local_out),
+          (long long)atomic64_read(&cnt_v4_postroute));
+}
+
+module_init(rabbit_init);
+module_exit(rabbit_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rabbit");
+MODULE_DESCRIPTION("Force UID 2017 traffic onto dev \"US\" at LOCAL_OUT with IPv4 exceptions");
diff --git a/developer/cc/Rabbit_no-op.kmod.c b/developer/cc/Rabbit_no-op.kmod.c
new file mode 100644 (file)
index 0000000..619f86a
--- /dev/null
@@ -0,0 +1,69 @@
+// rabbit_noop.c — Rabbit no-op netfilter interposer (Debian 12/Bookworm)
+// Build: out-of-tree module. Load/unload to verify hook coverage.
+// Behavior: increments counters, returns NF_ACCEPT. No packet mutation.
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/atomic.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+
+static atomic64_t cnt_v4_local_out  = ATOMIC_LONG_INIT(0);
+static atomic64_t cnt_v4_postroute  = ATOMIC_LONG_INIT(0);
+static atomic64_t cnt_v6_local_out  = ATOMIC_LONG_INIT(0);
+static atomic64_t cnt_v6_postroute  = ATOMIC_LONG_INIT(0);
+
+static unsigned int rabbit_v4_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st) {
+  if (st->hook == NF_INET_LOCAL_OUT)     atomic64_inc(&cnt_v4_local_out);
+  else if (st->hook == NF_INET_POST_ROUTING) atomic64_inc(&cnt_v4_postroute);
+  return NF_ACCEPT;
+}
+
+static unsigned int rabbit_v6_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st) {
+  if (st->hook == NF_INET_LOCAL_OUT)     atomic64_inc(&cnt_v6_local_out);
+  else if (st->hook == NF_INET_POST_ROUTING) atomic64_inc(&cnt_v6_postroute);
+  return NF_ACCEPT;
+}
+
+static struct nf_hook_ops rabbit_ops[] = {
+  { .hook = rabbit_v4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT,   .priority = NF_IP_PRI_FIRST },
+  { .hook = rabbit_v4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING,.priority = NF_IP_PRI_FIRST },
+  { .hook = rabbit_v6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT,   .priority = NF_IP6_PRI_FIRST },
+  { .hook = rabbit_v6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING,.priority = NF_IP6_PRI_FIRST },
+};
+
+static int __init rabbit_init(void) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+  int ret = nf_register_net_hooks(&init_net, rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#else
+  int ret = nf_register_hooks(rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#endif
+  if (ret)
+    pr_err("rabbit_noop: nf_register_* failed: %d\n", ret);
+  else
+    pr_info("rabbit_noop: loaded (no-op)\n");
+  return ret;
+}
+
+static void __exit rabbit_exit(void) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+  nf_unregister_net_hooks(&init_net, rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#else
+  nf_unregister_hooks(rabbit_ops, ARRAY_SIZE(rabbit_ops));
+#endif
+  pr_info("rabbit_noop: unload stats v4(lo=%lld,po=%lld) v6(lo=%lld,po=%lld)\n",
+          (long long)atomic64_read(&cnt_v4_local_out),
+          (long long)atomic64_read(&cnt_v4_postroute),
+          (long long)atomic64_read(&cnt_v6_local_out),
+          (long long)atomic64_read(&cnt_v6_postroute));
+}
+
+module_init(rabbit_init);
+module_exit(rabbit_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rabbit");
+MODULE_DESCRIPTION("Rabbit no-op netfilter interposer");
diff --git a/developer/scratchpad/.gitignore b/developer/scratchpad/.gitignore
new file mode 100644 (file)
index 0000000..120f485
--- /dev/null
@@ -0,0 +1,2 @@
+*
+!/.gitignore
index 9677018..dacde58 100644 (file)
@@ -6,7 +6,7 @@ include $(RT_INCOMMON)/make/environment_RT_1.mk
 
 .PHONY: usage 
 usage:
-       @printf "Usage: make [usage|information|all|lib|cli|kmod|clean]\n"; exit 2
+       @printf "Usage: make [usage|information|all|lib|cli|kmod|clean]\n"
 
 .PHONY: version 
 version:
@@ -29,13 +29,13 @@ information:
 .PHONY: all
 all: lib cli kmod
 
-.PHONY: lib
-lib:
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_lib_cli.mk lib
+.PHONY: library lib
+library lib:
+       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_cli.mk library
 
 .PHONY: cli
 cli:
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_lib_cli.mk cli
+       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_cli.mk cli
 
 .PHONY: kmod
 kmod:
@@ -43,7 +43,7 @@ kmod:
 
 .PHONY: clean
 clean:
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_lib_cli.mk clean
+       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_cli.mk clean
        @$(MAKE) -f $(RT_INCOMMON)/make/target_kmod.mk clean
 
 
diff --git a/document/conventions/release_howto.org b/document/conventions/release_howto.org
deleted file mode 100644 (file)
index c4ebedc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-See 'workflow.org'
diff --git a/document/quick_start_developer.sh b/document/quick_start_developer.sh
new file mode 100644 (file)
index 0000000..3f31a98
--- /dev/null
@@ -0,0 +1,26 @@
+# This is a document, not a script
+# There are more docs on IDEs, directory structure, etc.
+# Note especially the workflow document.
+
+#1. In a login shell
+
+  # enter the project environment as a developer
+  > cd Rabbit
+  > . env_developer
+
+  # run your IDE 
+  > emacs
+
+# 2. inside of an emacs shell, or IDE build scrit
+
+  # do you edits
+  # run local test experiments
+
+  > make all
+  > make release
+
+
+  # 3. In env_tester run more thorough test suite.  When satisfied make a release branch and tag it.
+  #    Release branches have consecutive major release numbers.
+       
+  
diff --git a/release/kmod/Rabbit_2017_count.ko b/release/kmod/Rabbit_2017_count.ko
new file mode 100644 (file)
index 0000000..6edb934
Binary files /dev/null and b/release/kmod/Rabbit_2017_count.ko differ