# `SmolNet.Loopback`
[🔗](https://github.com/ausimian/smolnet/blob/0.7.1/lib/smol_net/loopback.ex#L1)

A link process that feeds a stack's outbound packets back into itself.

SmolNet stacks are transport-neutral: a stack emits complete IPv4 or IPv6
packets to a link process, which carries them to wherever the other end of
the wire is. A loopback link is the degenerate case of that contract, where
the other end of the wire is the same stack. It lets one stack reach its own
addresses with no peer, no external transport, and no privileges, which makes
it the simplest way to exercise the library in an example, a doctest, or a
test case.

    {:ok, link, stack} =
      SmolNet.Loopback.start_link(
        addresses: [{{0, 0, 0, 0, 0, 0, 0, 1}, 128}, {{127, 0, 0, 1}, 8}]
      )

The link owns the stack it loops. `start_link/1` accepts the same options as
`SmolNet.start_stack/1` apart from `:egress`, which the link supplies, and
returns once the stack is running. Stopping the link stops the stack through
the stack's own `:link_down` policy. The link watches its stack with
`SmolNet.monitor/1`, so stopping the stack with `SmolNet.stop_stack/1`, or a
stack crash, stops the link.

Because the loop is an ordinary link process, packets re-enter through the
public `SmolNet.ingress/2` and are validated exactly like packets arriving
from a real transport. A packet the stack refuses, which in practice means a
packet offered while the stack is already busy with another feeder, is
dropped as a real link would drop it.

Given `:egress_credit`, the link grants back each batch once it has fed it
in, as a link with a bounded queue would; see `SmolNet.grant_egress/3`.

## Reaching a loopback address

A loopback link carries packets; it does not invent addresses. A stack
answers on the addresses it was configured with, so give it whichever
addresses the example needs. Conventional localhost addresses work, and so
does any other address the stack holds:

    {:ok, _link, stack} =
      SmolNet.Loopback.start_link(addresses: [{{192, 0, 2, 1}, 24}])

    {:ok, listener} = SmolNet.open(:inet, :stream, :tcp, stack: stack)
    :ok = SmolNet.bind(listener, %{family: :inet, addr: {192, 0, 2, 1}, port: 8080})
    :ok = SmolNet.listen(listener, 1)

A connection to `192.0.2.1:8080` on that stack now completes against its own
listener.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `stack`

```elixir
@spec stack(GenServer.server()) :: SmolNet.Stack.Ref.t()
```

Returns the stack for an already-running loopback link.

# `start_link`

```elixir
@spec start_link(keyword()) ::
  {:ok, pid(), SmolNet.Stack.Ref.t()} | :ignore | {:error, term()}
```

Starts a loopback link and the stack it loops.

Options are the `SmolNet.start_stack/1` options, plus an optional `:name` for
the link process itself. Supplying `:egress` is an error, because the link is
the stack's egress. Returns the link process and the stack reference as
`{:ok, link, stack}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
