Powered by AppSignal & Oban Pro

Echo

livebooks/tundra.livemd

Echo

Mix.install([
  {:tundra, "> 0.0.0"}
])

Section

defmodule Reflector do
  use GenServer

  @mtu 1500

  def start_link(_), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)

  @impl true
  def init(_args) do
    # Create a TUN device with the given address and options
    {:ok, state} =
      Tundra.create("fd11:b7b7:4360::2",
        dstaddr: "fd11:b7b7:4360::1",
        netmask: "ffff:ffff:ffff:ffff::",
        mtu: @mtu)
    {:ok, state, {:continue, :read}}
  end

  @impl true
  def handle_continue(:read, {dev, _} = state) do
    # Read a frame from the device
    case Tundra.recv(dev, @mtu, :nowait) do
      {:ok, data} ->
        {:noreply, state, {:continue, {:reflect, data}}}
      {:select, _} ->
        {:noreply, state}
    end
  end

  def handle_continue({:reflect, data}, {dev, _} = state) do
    # Swap the source and destination addresses of the IPv6 packet
    <<pre::binary-size(8),
      src::binary-size(16),
      dst::binary-size(16),
      rest::binary>> = data
    reflected = [pre, dst, src, rest]

    # Write the frame back to the device, assume it won't block
    :ok = Tundra.send(dev, reflected, :nowait)
    {:noreply, state, {:continue, :read}}
  end

  @impl true
  def handle_info({:"$socket", dev, :select, _}, {dev, _} = state) do
    # Handle 'input ready' notifications
    {:noreply, state, {:continue, :read}}
  end

end
{:ok, pid} = Reflector.start_link([])
GenServer.stop(pid)