Metadata-Version: 2.4
Name: PyTCP
Version: 3.0.7
Summary: Pure-Python, zero-dependency TCP/IP stack — Ethernet through RFC 9293 TCP — running in user space on a TAP/TUN interface, embeddable in-process or run as a daemon, with a Berkeley-sockets API.
Author-email: Sebastian Majewski <ccie18643@gmail.com>
License-Expression: GPL-3.0-or-later
Project-URL: Homepage, https://github.com/ccie18643/PyTCP
Project-URL: Bug Tracker, https://github.com/ccie18643/PyTCP/issues
Keywords: pytcp,stack,networking,tcp,ip,ipv4,ipv6,arp,ethernet,icmp
Classifier: Programming Language :: Python :: 3.14
Classifier: Development Status :: 3 - Alpha
Classifier: Operating System :: POSIX :: Linux
Classifier: Natural Language :: English
Classifier: Environment :: Console
Classifier: Topic :: System :: Networking
Classifier: Typing :: Typed
Requires-Python: >=3.14
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyTCP-net_addr==3.0.7
Requires-Dist: PyTCP-net_proto==3.0.7
Dynamic: license-file

# PyTCP

Pure-Python, zero-dependency TCP/IP stack — Ethernet through
RFC 9293 TCP — running in user space on a TAP/TUN interface,
embeddable in-process or run as a daemon, with a Berkeley-sockets API.

```python
from pytcp import socket, stack
```

## What it is

A full, RFC-grounded TCP/IP stack implemented entirely in Python:
Ethernet II / 802.3 (LLC/SNAP), ARP, IPv4 / IPv6 (with Hop-by-Hop /
Destination-Options / Routing / Fragment extension headers),
ICMPv4 / ICMPv6 (incl. Neighbor Discovery, MLDv2 with MLDv1 fallback,
and IGMP IPv4 multicast group membership), DHCPv4 and DHCPv6 clients,
UDP, and a RFC 9293 TCP with a real FSM, congestion control
(Reno / NewReno / CUBIC), SACK / timestamps / window-scaling, and a
BSD-sockets facade. It runs on a TAP/TUN interface in user space — no
kernel module, no privileged data path. It can be embedded in-process
as a library, or run as a **daemon** that out-of-process clients drive
over an AF_UNIX control boundary — the kernel/userspace split described
below.

The project's north star is **feature-equivalence with the Linux
host network stack**: where an RFC is unambiguous PyTCP follows it,
and where it is silent or offers a menu PyTCP picks the Linux
choice. Per-RFC adherence is audited under
[`docs/rfc/`](https://github.com/ccie18643/PyTCP/tree/master/docs/rfc).

## The three distributions

PyTCP is strictly layered into three independently-published dists
(one invariant: project folder == import name):

| Distribution | Import | Role |
|---|---|---|
| [`PyTCP-net_addr`](https://pypi.org/project/PyTCP-net_addr/) | `net_addr` | Address value types (IPv4/IPv6/MAC, networks, masks, wildcards, interface-addresses). |
| [`PyTCP-net_proto`](https://pypi.org/project/PyTCP-net_proto/) | `net_proto` | Protocol packet parse / assemble / validate. |
| **`PyTCP`** | `pytcp` | The running stack: subsystems/threads, sockets, FIB, ARP/ND caches, RX/TX rings. |

Installing `PyTCP` pulls the other two automatically (lockstep
version pin).

## Runtime architecture

```
TAP/TUN fd ─> RxRing ─> PacketHandler (per protocol, RX) ─> Socket queues / ARP+ND caches / fragment store
           <─ TxRing <─ PacketHandler (per protocol, TX) <─ Socket send / ND / DHCP / ACD
```

- **`Subsystem` base** — every background service (RX/TX rings,
  neighbor caches, timer, DHCPv4 / DHCPv6 clients, link-local / ACD)
  extends `Subsystem` and runs its own thread with an event-driven
  loop.
- **Packet handlers** — RX and TX paths are composed from
  per-protocol sub-handlers (`packet_handler__<proto>__<rx|tx>.py`).
  Every branch bumps a per-protocol stat counter for observability.
- **Event-driven timer** — a heap-based deadline scheduler (no
  polling tick); subsystems register deadlines and are woken on the
  nearest one.
- **Per-interface model** — a `PacketHandler` *is* an interface. A
  multi-homed host runs one handler per interface; global tables
  (routing FIB, socket table, neighbor caches) are shared and
  lock-guarded.

### Free-threaded (no-GIL) safety

Per-interface state is partitioned (single-writer TX ring hand-off);
the shared global tables (`RouteTable`, `SocketTable`,
`InterfaceTable`) guard their compound (check-then-act) operations
with a small `threading.Lock` and hand readers consistent snapshots.
Single built-in dict/list ops are left lock-free (individually
atomic).

## Control-plane APIs (the Phase-3 kernel/userspace boundary)

Consumers talk to the stack only through sanctioned surfaces — never
by reaching into runtime internals — mirroring how a Linux process
talks to its kernel:

| API | Linux equivalent |
|---|---|
| `pytcp.socket` — BSD `socket()` factory + methods (TCP / UDP / raw / `AF_PACKET`) | `socket(2)` |
| `pytcp.stack.sysctl` — runtime-tunable policy registry | `/proc/sys/net/` |
| `pytcp.stack.link` — per-interface MAC / MTU / state / counters | `ip link` / `RTM_*LINK` |
| `pytcp.stack.address` — assign / remove IPv4 / IPv6 host addresses | `ip addr` / `RTM_*ADDR` |
| `pytcp.stack.route` — add / remove / list routes (FIB); `Route` / `RouteProtocol` / `RouteScope` | `ip route` / `RTM_*ROUTE` |
| `pytcp.stack.neighbor` — static ARP / ND entries, cache flush | `ip neighbor` / `RTM_*NEIGH` |
| read-only snapshots (route table, neighbor cache, socket list, counters) | `/proc/net/*`, `ss` |

### Lifecycle

`stack.init(...)` builds the singletons, `stack.add_interface(...)` /
`stack.remove_interface(...)` attach / detach interfaces at runtime
(RTNETLINK `RTM_NEWLINK` / `RTM_DELLINK` semantics, including the
address / route / neighbor / session teardown cascade), `stack.start()`
spawns the subsystem threads, and `stack.stop()` winds them down.
A stack can `init()` with zero interfaces and gain them later — the
daemon / multi-homed shape.

### Sockets

`pytcp.socket` mirrors the stdlib `socket` module: a `socket(...)`
factory returns `TcpSocket` / `UdpSocket` / `RawSocket` /
`PacketSocket`, with `bind` / `listen` / `accept` / `connect` /
`send` / `recv` / `close`, `fileno()` + eventfd for `selectors`
integration, blocking & non-blocking modes, errno-mapped `OSError`,
`getaddrinfo`, common `setsockopt` options, IPv4/IPv6 multicast group
membership and source-filter options (`IP_ADD_MEMBERSHIP`,
`IP_ADD_SOURCE_MEMBERSHIP`, `IPV6_JOIN_GROUP`, …), and an
`IP_RECVERR` / `MSG_ERRQUEUE` error queue. Stdlib-parity constants
(`AF_INET`, `SOCK_STREAM`, `IP_*`, `SO_*`, `MSG_*`) are exposed as
bare module names backed by `IntEnum`s.

## Daemon mode — out-of-process clients

The stack runs as a **daemon**: a normal in-process stack that also
listens on an AF_UNIX control socket, so a **separate process** opens
sockets and drives the control-plane APIs through `pytcp.client` —
exactly the way a Linux process talks to the kernel. The client never
boots the stack; it holds real, `selectors`-pollable socket fds handed
to it across the boundary via `SCM_RIGHTS`.

Start the daemon (it owns the TAP interface); the first-class entry
point ships in the package as `python -m pytcp.daemon` (or the `pytcpd`
console script after install), defaulting the control socket to
`$XDG_RUNTIME_DIR/pytcp.sock`:

```bash
python -m pytcp.daemon --ipc-socket /tmp/pytcp.sock
```

Then, from any other process — note it imports `pytcp.client`, boots
no stack, and calls no `stack.init()`:

```python
from pytcp.client import connect
from pytcp.socket import AddressFamily, SocketType

with connect(socket_path="/tmp/pytcp.sock") as client:
    sock = client.socket(AddressFamily.INET4, SocketType.STREAM)
    sock.connect(("10.0.1.1", 7))   # a real, selectable fd backs this socket
    sock.send(b"hello")
    print(sock.recv(5))
```

The same `client.socket(...)` factory returns UDP / raw / `AF_PACKET`
sockets, and `client.sysctl` / `.route` / `.link` / `.address` /
`.neighbor` / `.membership` mirror the in-process control APIs across
the boundary. See
[`examples/client__tcp_echo_ipc.py`](https://github.com/ccie18643/PyTCP/blob/master/examples/client__tcp_echo_ipc.py).

## Install

```bash
pip install PyTCP
```

Brings in `PyTCP-net_proto` and `PyTCP-net_addr` — no other
runtime dependencies (the whole stack is stdlib-only).
Fully typed (ships `py.typed`, PEP 561); strict-mypy clean.

## Running the stack

Running needs one or more TAP/TUN interfaces (root for interface /
bridge setup). Bridged TAP interfaces (Ethernet) are created on the
`br0` bridge, so the bridge comes first:

```bash
make bridge      # create the br0 bridge (sudo)
make tap7        # create tap7, add it to br0 (sudo)
make tap9        # create a second tap, tap9, on br0 (sudo)
make run         # run the stack on tap7
make run_multi   # multi-interface demo (runs on tap7 + tap9)
```

Point-to-point TUN interfaces (IP), each created pre-addressed and
needing no bridge, are also available — `make tun3`
(172.16.1.1/24, 2001:db8:1::1/64) and `make tun5`
(172.16.2.1/24, 2001:db8:2::1/64) set up the host side, and
`make run_tun` / `make run_tun5` run the stack on the matching device
(taking the `.2` host in each subnet). A stack can `init()` with zero
interfaces and add / remove them at runtime, so any mix of taps and
tuns can be attached to one running stack.

PyTCP is consumed as a library through the `stack` lifecycle API and
the `pytcp.socket` BSD-sockets API. See
[`examples/`](https://github.com/ccie18643/PyTCP/tree/master/examples) — `examples/stack.py` is the complete
runnable reference (TAP/TUN open, `stack.init(...)`, multi-interface
bind, runtime interface removal on SIGUSR1).

## Requirements

Python **3.14+**, Linux (TAP/TUN), POSIX.

## Current state (3.0.7)

- ~210 source modules; 3.0.7 adds the kernel/userspace IPC layer (an
  `ipc` AF_UNIX RPC + SCM_RIGHTS fd-passing core, a `client`
  out-of-process mirror, and a first-class `daemon` entry point). The
  pytcp suite runs ~4,000 unit + integration tests (the full repo
  suite, across all three packages + examples, is ~12,500). Lint clean
  (codespell + isort + black + flake8 + mypy strict + pylint).
- Host-stack feature-complete (North Star Phase 1), now reachable both
  in-process and over an out-of-process **daemon** boundary (AF_UNIX
  control plane + SCM_RIGHTS socket-fd passing for TCP / UDP / raw /
  `AF_PACKET`). Phase-2 router/forwarding sits behind the
  `forward_or_deliver` seam as a stub. Authoring contracts in
  [`.claude/rules/pytcp.md`](https://github.com/ccie18643/PyTCP/blob/master/.claude/rules/pytcp.md); per-RFC
  adherence in [`docs/rfc/`](https://github.com/ccie18643/PyTCP/tree/master/docs/rfc).

## License

GPL-3.0-or-later. PyTCP by Sebastian Majewski.
